mirror of
				https://github.com/KevinMidboe/TinyGSM.git
				synced 2025-10-29 18:00:18 +00:00 
			
		
		
		
	Merge pull request #285 from EnviroDIY/master
Goodbye virtual class (hello, pre-processor macros!)
This commit is contained in:
		
							
								
								
									
										26
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -18,17 +18,19 @@ env: | ||||
|     - PLATFORMIO_CI_SRC=tools/FactoryReset | ||||
|  | ||||
|     # Arduino test | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_A6'      --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev --board=mayfly" | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_BG96'    --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev --board=mayfly" | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_ESP8266' --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev --board=mayfly" | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_M95'     --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev --board=mayfly" | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_M590'    --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev --board=mayfly" | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_MC60'    --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev --board=mayfly" | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SIM800'  --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev --board=mayfly" | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SIM808'  --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev --board=mayfly" | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SIM7000' --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev --board=mayfly" | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_UBLOX'   --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev --board=mayfly" | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_XBEE'    --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev --board=mayfly" | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_A6'      --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev" | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_BG96'    --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev" | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_ESP8266' --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev" | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_M95'     --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev" | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_M590'    --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev" | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_MC60'    --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev" | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SIM800'  --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev" | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SIM808'  --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev" | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SIM7000' --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev" | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_UBLOX'   --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev" | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SARAR4'  --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev" | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_XBEE'    --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev" | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SEQUANS_MONARCH'    --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev" | ||||
|  | ||||
|     # Energia test | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_A6'      --project-option='framework=energia' --board=lplm4f120h5qr" | ||||
| @@ -41,7 +43,9 @@ env: | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SIM808'  --project-option='framework=energia' --board=lplm4f120h5qr" | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SIM7000' --project-option='framework=energia' --board=lplm4f120h5qr" | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_UBLOX'   --project-option='framework=energia' --board=lplm4f120h5qr" | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SARAR4'  --project-option='framework=energia' --board=lplm4f120h5qr" | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_XBEE'    --project-option='framework=energia' --board=lplm4f120h5qr" | ||||
|     - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SEQUANS_MONARCH'    --project-option='framework=energia' --board=lplm4f120h5qr" | ||||
|  | ||||
|     # Disabled due to a bug in Energia readBytes implementation | ||||
|     #- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_XBEE'    --project-option='framework=energia' --board=lplm4f120h5qr" | ||||
|   | ||||
							
								
								
									
										41
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								README.md
									
									
									
									
									
								
							| @@ -35,31 +35,31 @@ TinyGSM also pulls data gently from the modem (whenever possible), so it can ope | ||||
|  | ||||
| ## Features | ||||
|  | ||||
| Feature \ Modem              | SIM8xx | u-Blox | A6/A7/A20 | Neoway M590| ESP8266 |Digi XBee|Quectel BG96|Quectel M95|Quectel MC60(E)| SIM7000 | | ||||
| ---                          | ---    | ---    | ---       | ---        | ---     | ---     | ---        | ---       | ---           | ---     | | ||||
| Feature \ Modem              | SIM8xx | u-Blox | A6/A7/A20 | Neoway M590| ESP8266 |Digi XBee|Quectel BG96|Quectel M95|Quectel MC60(E)| SIM7000 | Monarch | | ||||
| ---                          | ---    | ---    | ---       | ---        | ---     | ---     | ---        | ---       | ---           | ---     | ---     | | ||||
| **Data connections** | ||||
| TCP (HTTP, MQTT, Blynk, ...) | ✔      | ✔      | ✔         | ✔         | ✔        | ✔       | ✔          | ✔         | ✔             |         | | ||||
| UDP                          | ◌      | ◌      |           |           |          | ◌       | ◌          |           |               | ◌       | | ||||
| SSL/TLS (HTTPS)              | ✔¹     | ✔      | x         | x         | ✔        | ✔       | ◌          |           |               | ◌       | | ||||
| TCP (HTTP, MQTT, Blynk, ...) | ✔      | ✔      | ✔         | ✔         | ✔        | ✔       | ✔          | ✔         | ✔             | ✔       | ✔       | | ||||
| UDP                          | ◌      | ◌      |           |           |          | ◌       | ◌          |           |               | ◌       | ◌       | | ||||
| SSL/TLS (HTTPS)              | ✔¹     | ✔      | x         | x         | ✔        | ✔       | ◌          |           |               | ◌       | ✔       | | ||||
| **USSD** | ||||
| Sending USSD requests        | ✔      |        | ✔         | ✔         | x        |          |             |         |               | ✔       | | ||||
| Decoding 7,8,16-bit response | ✔      |        | ✔         | ✔         | x        |          |             |         |               | ✔       | | ||||
| Sending USSD requests        | ✔      |        | ✔         | ✔         | x        |          |             |         |               | ✔       |         | | ||||
| Decoding 7,8,16-bit response | ✔      |        | ✔         | ✔         | x        |          |             |         |               | ✔       |         | | ||||
| **SMS** | ||||
| Sending                      | ✔      | ✔      | ✔         | ✔         | x        | ✔        | ✔          | ✔        | ✔             | ✔       | | ||||
| Sending Unicode              | ✔      |        | ◌         | x         | x        |          | ✔           | ✔       | ✔             | ✔       | | ||||
| Reading                      |        |        |           |           | x        |          |             |         |               |         | | ||||
| Incoming message event       |        |        |           | ?         | x        |          |             |         |               |         | | ||||
| Sending                      | ✔      | ✔      | ✔         | ✔         | x        | ✔        | ✔          | ✔        | ✔             | ✔       | ✔       | | ||||
| Sending Unicode              | ✔      |        | ◌         | x         | x        |          | ✔           | ✔       | ✔             | ✔       |         | | ||||
| Reading                      |        |        |           |           | x        |          |             |         |               |         |         | | ||||
| Incoming message event       |        |        |           | ?         | x        |          |             |         |               |         |         | | ||||
| **Calls** | ||||
| Dial, hangup                 | ✔      |        | ✔         | x         | x        | x        |             |         |               |         | | ||||
| Receiving calls              | ✔      |        | ✔         | x         | x        | x        |             |         |               |         | | ||||
| Incoming event (RING)        | ◌      |        | ◌         | x         | x        | x        |             |         |               |         | | ||||
| DTMF sending                 | ✔      |        | ✔         | x         | x        | x        |             |         |               |         | | ||||
| DTMF decoding                | ◌      |        | x         | x         | x        | x        |             |         |               |         | | ||||
| Dial, hangup                 | ✔      |        | ✔         | x         | x        | x        |             |         |               |         |         | | ||||
| Receiving calls              | ✔      |        | ✔         | x         | x        | x        |             |         |               |         |         | | ||||
| Incoming event (RING)        | ◌      |        | ◌         | x         | x        | x        |             |         |               |         |         | | ||||
| DTMF sending                 | ✔      |        | ✔         | x         | x        | x        |             |         |               |         |         | | ||||
| DTMF decoding                | ◌      |        | x         | x         | x        | x        |             |         |               |         |         | | ||||
| **Location** | ||||
| GSM location service         | ✔      | ✔      | x         | x         | x        | x        |             | x       | ✔             | ✔       | | ||||
| GPS/GNSS                     | ✔¹     | x      | ◌¹        | x         | x        | x        |             | x       |               | ✔       | | ||||
| GSM location service         | ✔      | ✔      | x         | x         | x        | x        |             | x       | ✔             | ✔       | x       | | ||||
| GPS/GNSS                     | ✔¹     | x      | ◌¹        | x         | x        | x        |             | x       |               | ✔       | x       | | ||||
| **Credits** | ||||
| Primary Author/Contributor   |[vshymanskyy](https://github.com/vshymanskyy)|[vshymanskyy](https://github.com/vshymanskyy)|[vshymanskyy](https://github.com/vshymanskyy)|[vshymanskyy](https://github.com/vshymanskyy)|[vshymanskyy](https://github.com/vshymanskyy)|[SRGDamia1](https://github.com/SRGDamia1/)|[vshymanskyy](https://github.com/vshymanskyy)  |[replicadeltd](https://github.com/replicadeltd)|[V1pr](https://github.com/V1pr)|[captFuture](https://github.com/captFuture/)| | ||||
| Primary Author/Contributor   |[vshymanskyy](https://github.com/vshymanskyy)|[vshymanskyy](https://github.com/vshymanskyy)|[vshymanskyy](https://github.com/vshymanskyy)|[vshymanskyy](https://github.com/vshymanskyy)|[vshymanskyy](https://github.com/vshymanskyy)|[SRGDamia1](https://github.com/SRGDamia1/)|[vshymanskyy](https://github.com/vshymanskyy)  |[replicadeltd](https://github.com/replicadeltd)|[V1pr](https://github.com/V1pr)|[captFuture](https://github.com/captFuture/)|[nootropicdesign](https://github.com/nootropicdesign/)| | ||||
|  | ||||
| ✔ - implemented  ◌ - planned  x - not available on this modem | ||||
| ¹ - only some device models or firmware revisions have this feature (SIM8xx R14.18, A7, etc.) | ||||
| @@ -73,6 +73,7 @@ Primary Author/Contributor   |[vshymanskyy](https://github.com/vshymanskyy)|[vsh | ||||
| - Digi XBee WiFi and Cellular (using XBee command mode) | ||||
| - Neoway M590 | ||||
| - u-blox Cellular Modems (many modules including LEON-G100, LISA-U2xx, SARA-G3xx, SARA-U2xx, TOBY-L2xx, LARA-R2xx, MPCI-L2xx, SARA-R4xx, SARA-N4xx, _but NOT SARA-N2xx_) | ||||
| - Sequans Monarch LTE Cat M1/NB1 ***(beta)*** | ||||
| - Quectel BG96 ***(alpha)*** | ||||
| - Quectel M95 ***(alpha)*** | ||||
| - Quectel MC60 ***(alpha)*** | ||||
| @@ -89,9 +90,9 @@ Primary Author/Contributor   |[vshymanskyy](https://github.com/vshymanskyy)|[vsh | ||||
| - ... other modules, based on supported modems. Some boards require [**special configuration**](https://github.com/vshymanskyy/TinyGSM/wiki/Board-configuration). | ||||
|  | ||||
| More modems may be supported later: | ||||
| - [ ] Sequans Monarch LTE Cat M1/NB1 | ||||
| - [ ] Quectel M10, UG95 | ||||
| - [ ] SIMCom SIM5320, SIM5360, SIM5216 | ||||
| - [ ] SIMCom SIM7020, SIM7100 | ||||
| - [ ] Telit GL865 | ||||
| - [ ] ZTE MG2639 | ||||
| - [ ] Hi-Link HLK-RM04 | ||||
|   | ||||
| @@ -10,11 +10,13 @@ | ||||
|  **************************************************************/ | ||||
|  | ||||
| // Select your modem: | ||||
| //#define TINY_GSM_MODEM_SIM800 | ||||
| #define TINY_GSM_MODEM_SIM800 | ||||
| // #define TINY_GSM_MODEM_SIM808 | ||||
| // #define TINY_GSM_MODEM_SIM868 | ||||
| // #define TINY_GSM_MODEM_SIM900 | ||||
| #define TINY_GSM_MODEM_SIM7000 | ||||
| // #define TINY_GSM_MODEM_SIM7000 | ||||
| // #define TINY_GSM_MODEM_UBLOX | ||||
| // #define TINY_GSM_MODEM_SARAR4 | ||||
| // #define TINY_GSM_MODEM_M95 | ||||
| // #define TINY_GSM_MODEM_BG96 | ||||
| // #define TINY_GSM_MODEM_A6 | ||||
| @@ -24,8 +26,9 @@ | ||||
| // #define TINY_GSM_MODEM_MC60E | ||||
| // #define TINY_GSM_MODEM_ESP8266 | ||||
| // #define TINY_GSM_MODEM_XBEE | ||||
| // #define TINY_GSM_MODEM_SEQUANS_MONARCH | ||||
|  | ||||
| // Set serial for debug console (to the Serial Monitor, speed 115200) | ||||
| // Set serial for debug console (to the Serial Monitor, default speed 115200) | ||||
| #define SerialMon Serial | ||||
|  | ||||
| // Set serial for AT commands (to the module) | ||||
| @@ -36,10 +39,13 @@ | ||||
| //#include <SoftwareSerial.h> | ||||
| //SoftwareSerial SerialAT(2, 3); // RX, TX | ||||
|  | ||||
|  | ||||
| // See all AT commands, if wanted | ||||
| //#define DUMP_AT_COMMANDS | ||||
|  | ||||
| // See the debugging, if wanted | ||||
| #define TINY_GSM_DEBUG SerialMon | ||||
|  | ||||
| // Range to attempt to autobaud | ||||
| #define GSM_AUTOBAUD_MIN 9600 | ||||
| #define GSM_AUTOBAUD_MAX 38400 | ||||
|  | ||||
| @@ -47,6 +53,7 @@ | ||||
|  * Test enabled | ||||
|  */ | ||||
| #define TINY_GSM_USE_GPRS true | ||||
| #define TINY_GSM_USE_WIFI false | ||||
| #define TINY_GSM_USE_CALL true | ||||
| #define TINY_GSM_USE_SMS true | ||||
| #define TINY_GSM_USE_USSD true | ||||
| @@ -65,6 +72,8 @@ | ||||
| const char apn[]  = "YourAPN"; | ||||
| const char user[] = ""; | ||||
| const char pass[] = ""; | ||||
| const char wifiSSID[]  = "YourSSID"; | ||||
| const char wifiPass[] = "SSIDpw"; | ||||
|  | ||||
| #include <TinyGsmClient.h> | ||||
|  | ||||
| @@ -82,11 +91,18 @@ void setup() { | ||||
|   delay(10); | ||||
|  | ||||
|   // Set your reset, enable, power pins here | ||||
|   pinMode(20, OUTPUT); | ||||
|   digitalWrite(20, HIGH); | ||||
|  | ||||
|   pinMode(23, OUTPUT); | ||||
|   digitalWrite(23, HIGH); | ||||
|  | ||||
|   DBG("Wait..."); | ||||
|   delay(3000); | ||||
|  | ||||
|   // Set GSM module baud rate | ||||
|   TinyGsmAutoBaud(SerialAT,GSM_AUTOBAUD_MIN,GSM_AUTOBAUD_MAX); | ||||
|   // SerialAT.begin(9600); | ||||
| } | ||||
|  | ||||
| void loop() { | ||||
| @@ -95,6 +111,7 @@ void loop() { | ||||
|   // To skip it, call init() instead of restart() | ||||
|   DBG("Initializing modem..."); | ||||
|   if (!modem.restart()) { | ||||
|   // if (!modem.init()) { | ||||
|     DBG("Failed to restart modem, delaying 10s and retrying"); | ||||
|     delay(3000); | ||||
|     // restart autobaud in case GSM just rebooted | ||||
| @@ -106,10 +123,22 @@ void loop() { | ||||
|   String modemInfo = modem.getModemInfo(); | ||||
|   DBG("Modem:", modemInfo); | ||||
|  | ||||
| #if TINY_GSM_USE_GPRS | ||||
|   // Unlock your SIM card with a PIN if needed | ||||
|   if ( GSM_PIN && modem.getSimStatus() != 3 ) { | ||||
|     modem.simUnlock(GSM_PIN); | ||||
|   } | ||||
| #endif | ||||
|  | ||||
| #if TINY_GSM_USE_WIFI | ||||
|   SerialMon.print(F("Setting SSID/password...")); | ||||
|   if (!modem.networkConnect(wifiSSID, wifiPass)) { | ||||
|     SerialMon.println(" fail"); | ||||
|     delay(10000); | ||||
|     return; | ||||
|   } | ||||
|   SerialMon.println(" OK"); | ||||
| #endif | ||||
|  | ||||
|   DBG("Waiting for network..."); | ||||
|   if (!modem.waitForNetwork()) { | ||||
| @@ -221,6 +250,11 @@ void loop() { | ||||
|   } | ||||
| #endif | ||||
|  | ||||
| #if TINY_GSM_USE_WIFI | ||||
|   modem.networkDisconnect(); | ||||
|   DBG("WiFi disconnected"); | ||||
| #endif | ||||
|  | ||||
| #if TINY_GSM_POWERDOWN | ||||
|   // Try to power-off (modem may decide to restart automatically) | ||||
|   // To turn off modem completely, please use Reset/Enable pins | ||||
|   | ||||
| @@ -30,11 +30,12 @@ | ||||
|  | ||||
| // Select your modem: | ||||
| #define TINY_GSM_MODEM_SIM800 | ||||
| // #define TINY_GSM_MODEM_SIM900 | ||||
| // #define TINY_GSM_MODEM_SIM808 | ||||
| // #define TINY_GSM_MODEM_SIM868 | ||||
| // #define TINY_GSM_MODEM_SIM900 | ||||
| // #define TINY_GSM_MODEM_SIM7000 | ||||
| // #define TINY_GSM_MODEM_UBLOX | ||||
| // #define TINY_GSM_MODEM_SARAR4 | ||||
| // #define TINY_GSM_MODEM_M95 | ||||
| // #define TINY_GSM_MODEM_BG96 | ||||
| // #define TINY_GSM_MODEM_A6 | ||||
| @@ -44,6 +45,7 @@ | ||||
| // #define TINY_GSM_MODEM_MC60E | ||||
| // #define TINY_GSM_MODEM_ESP8266 | ||||
| // #define TINY_GSM_MODEM_XBEE | ||||
| // #define TINY_GSM_MODEM_SEQUANS_MONARCH | ||||
|  | ||||
| #include <TinyGsmClient.h> | ||||
| #include <BlynkSimpleSIM800.h> | ||||
|   | ||||
| @@ -15,11 +15,12 @@ | ||||
|  | ||||
| // Select your modem: | ||||
| #define TINY_GSM_MODEM_SIM800 | ||||
| // #define TINY_GSM_MODEM_SIM900 | ||||
| // #define TINY_GSM_MODEM_SIM808 | ||||
| // #define TINY_GSM_MODEM_SIM868 | ||||
| // #define TINY_GSM_MODEM_SIM900 | ||||
| // #define TINY_GSM_MODEM_SIM7000 | ||||
| // #define TINY_GSM_MODEM_UBLOX | ||||
| // #define TINY_GSM_MODEM_SARAR4 | ||||
| // #define TINY_GSM_MODEM_M95 | ||||
| // #define TINY_GSM_MODEM_BG96 | ||||
| // #define TINY_GSM_MODEM_A6 | ||||
| @@ -29,6 +30,7 @@ | ||||
| // #define TINY_GSM_MODEM_MC60E | ||||
| // #define TINY_GSM_MODEM_ESP8266 | ||||
| // #define TINY_GSM_MODEM_XBEE | ||||
| // #define TINY_GSM_MODEM_SEQUANS_MONARCH | ||||
|  | ||||
| // Increase RX buffer if needed | ||||
| #define TINY_GSM_RX_BUFFER 1024 | ||||
|   | ||||
| @@ -19,11 +19,12 @@ | ||||
|  | ||||
| // Select your modem: | ||||
| #define TINY_GSM_MODEM_SIM800 | ||||
| // #define TINY_GSM_MODEM_SIM900 | ||||
| // #define TINY_GSM_MODEM_SIM808 | ||||
| // #define TINY_GSM_MODEM_SIM868 | ||||
| // #define TINY_GSM_MODEM_SIM900 | ||||
| // #define TINY_GSM_MODEM_SIM7000 | ||||
| // #define TINY_GSM_MODEM_UBLOX | ||||
| // #define TINY_GSM_MODEM_SARAR4 | ||||
| // #define TINY_GSM_MODEM_M95 | ||||
| // #define TINY_GSM_MODEM_BG96 | ||||
| // #define TINY_GSM_MODEM_A6 | ||||
| @@ -32,6 +33,8 @@ | ||||
| // #define TINY_GSM_MODEM_MC60 | ||||
| // #define TINY_GSM_MODEM_MC60E | ||||
| // #define TINY_GSM_MODEM_ESP8266 | ||||
| // #define TINY_GSM_MODEM_XBEE | ||||
| // #define TINY_GSM_MODEM_SEQUANS_MONARCH | ||||
|  | ||||
| // Increase RX buffer to capture the entire response | ||||
| // Chips without internal buffering (A6/A7, ESP8266, M590) | ||||
| @@ -39,6 +42,9 @@ | ||||
| // else data will be lost (and the http library will fail). | ||||
| #define TINY_GSM_RX_BUFFER 650 | ||||
|  | ||||
| // See all AT commands, if wanted | ||||
| //#define DUMP_AT_COMMANDS | ||||
|  | ||||
| // See the debugging, if wanted | ||||
| //#define TINY_GSM_DEBUG Serial | ||||
| //#define LOGGING | ||||
| @@ -49,12 +55,10 @@ | ||||
| #include <TinyGsmClient.h> | ||||
| #include <ArduinoHttpClient.h> | ||||
|  | ||||
| // Uncomment this if you want to see all AT commands | ||||
| //#define DUMP_AT_COMMANDS | ||||
|  | ||||
| // Set serial for debug console (to the Serial Monitor, default speed 115200) | ||||
| #define SerialMon Serial | ||||
|  | ||||
| // Set serial for AT commands (to the module) | ||||
| // Use Hardware Serial on Mega, Leonardo, Micro | ||||
| #define SerialAT Serial1 | ||||
|  | ||||
| @@ -62,6 +66,11 @@ | ||||
| //#include <SoftwareSerial.h> | ||||
| //SoftwareSerial SerialAT(2, 3); // RX, TX | ||||
|  | ||||
| #define TINY_GSM_USE_GPRS true | ||||
| #define TINY_GSM_USE_WIFI false | ||||
|  | ||||
| // set GSM PIN, if any | ||||
| #define GSM_PIN "" | ||||
|  | ||||
| // Your GPRS credentials | ||||
| // Leave empty, if missing user or pass | ||||
| @@ -91,7 +100,15 @@ void setup() { | ||||
|   // Set console baud rate | ||||
|   SerialMon.begin(115200); | ||||
|   delay(10); | ||||
|   SerialMon.println(F("Wait...")); | ||||
|  | ||||
|   // Set your reset, enable, power pins here | ||||
|   pinMode(20, OUTPUT); | ||||
|   digitalWrite(20, HIGH); | ||||
|  | ||||
|   pinMode(23, OUTPUT); | ||||
|   digitalWrite(23, HIGH); | ||||
|  | ||||
|   SerialMon.println("Wait..."); | ||||
|  | ||||
|   // Set GSM module baud rate | ||||
|   SerialAT.begin(115200); | ||||
| @@ -99,11 +116,11 @@ void setup() { | ||||
|  | ||||
|   // Restart takes quite some time | ||||
|   // To skip it, call init() instead of restart() | ||||
|   SerialMon.println(F("Initializing modem...")); | ||||
|   SerialMon.println("Initializing modem..."); | ||||
|   modem.restart(); | ||||
|  | ||||
|   String modemInfo = modem.getModemInfo(); | ||||
|   SerialMon.print(F("Modem: ")); | ||||
|   SerialMon.print("Modem: "); | ||||
|   SerialMon.println(modemInfo); | ||||
|  | ||||
|   // Unlock your SIM card with a PIN | ||||
| @@ -112,17 +129,17 @@ void setup() { | ||||
|  | ||||
| void loop() { | ||||
|  | ||||
|   if (modem.hasWifi()) { | ||||
|     SerialMon.print(F("Setting SSID/password...")); | ||||
|     if (!modem.networkConnect(wifiSSID, wifiPass)) { | ||||
|       SerialMon.println(" fail"); | ||||
|       delay(10000); | ||||
|       return; | ||||
|     } | ||||
|     SerialMon.println(" OK"); | ||||
| #if TINY_GSM_USE_WIFI | ||||
|   SerialMon.print(F("Setting SSID/password...")); | ||||
|   if (!modem.networkConnect(wifiSSID, wifiPass)) { | ||||
|     SerialMon.println(" fail"); | ||||
|     delay(10000); | ||||
|     return; | ||||
|   } | ||||
|   SerialMon.println(" OK"); | ||||
| #endif | ||||
|  | ||||
|   SerialMon.print(F("Waiting for network...")); | ||||
|   SerialMon.print("Waiting for network..."); | ||||
|   if (!modem.waitForNetwork()) { | ||||
|     SerialMon.println(" fail"); | ||||
|     delay(10000); | ||||
| @@ -130,7 +147,11 @@ void loop() { | ||||
|   } | ||||
|   SerialMon.println(" OK"); | ||||
|  | ||||
|   if (modem.hasGPRS()) { | ||||
|   if (modem.isNetworkConnected()) { | ||||
|     SerialMon.print("Network connected"); | ||||
|   } | ||||
|  | ||||
| #if TINY_GSM_USE_GPRS | ||||
|     SerialMon.print(F("Connecting to ")); | ||||
|     SerialMon.print(apn); | ||||
|     if (!modem.gprsConnect(apn, gprsUser, gprsPass)) { | ||||
| @@ -139,7 +160,7 @@ void loop() { | ||||
|       return; | ||||
|     } | ||||
|     SerialMon.println(" OK"); | ||||
|   } | ||||
| #endif | ||||
|  | ||||
|   SerialMon.print(F("Performing HTTP GET request... ")); | ||||
|   int err = http.get(resource); | ||||
| @@ -185,8 +206,14 @@ void loop() { | ||||
|   http.stop(); | ||||
|   SerialMon.println(F("Server disconnected")); | ||||
|  | ||||
|   modem.gprsDisconnect(); | ||||
|   SerialMon.println(F("GPRS disconnected")); | ||||
| #if TINY_GSM_USE_WIFI | ||||
|     modem.networkDisconnect(); | ||||
|     SerialMon.println(F("WiFi disconnected")); | ||||
| #endif | ||||
| #if TINY_GSM_USE_GPRS | ||||
|     modem.gprsDisconnect(); | ||||
|     SerialMon.println(F("GPRS disconnected")); | ||||
| #endif | ||||
|  | ||||
|   // Do nothing forevermore | ||||
|   while (true) { | ||||
|   | ||||
| @@ -21,6 +21,7 @@ | ||||
| // #define TINY_GSM_MODEM_SIM808 | ||||
| // #define TINY_GSM_MODEM_SIM868 | ||||
| // #define TINY_GSM_MODEM_UBLOX | ||||
| // #define TINY_GSM_MODEM_SARAR4 | ||||
| // #define TINY_GSM_MODEM_ESP8266 | ||||
|  | ||||
| // Increase RX buffer to capture the entire response | ||||
| @@ -29,6 +30,9 @@ | ||||
| // else data will be lost (and the http library will fail). | ||||
| #define TINY_GSM_RX_BUFFER 650 | ||||
|  | ||||
| // See all AT commands, if wanted | ||||
| //#define DUMP_AT_COMMANDS | ||||
|  | ||||
| // See the debugging, if wanted | ||||
| //#define TINY_GSM_DEBUG Serial | ||||
| //#define LOGGING | ||||
| @@ -39,12 +43,10 @@ | ||||
| #include <TinyGsmClient.h> | ||||
| #include <ArduinoHttpClient.h> | ||||
|  | ||||
| // Uncomment this if you want to see all AT commands | ||||
| //#define DUMP_AT_COMMANDS | ||||
|  | ||||
| // Set serial for debug console (to the Serial Monitor, default speed 115200) | ||||
| #define SerialMon Serial | ||||
|  | ||||
| // Set serial for AT commands (to the module) | ||||
| // Use Hardware Serial on Mega, Leonardo, Micro | ||||
| #define SerialAT Serial1 | ||||
|  | ||||
| @@ -52,6 +54,11 @@ | ||||
| //#include <SoftwareSerial.h> | ||||
| //SoftwareSerial SerialAT(2, 3); // RX, TX | ||||
|  | ||||
| #define TINY_GSM_USE_GPRS true | ||||
| #define TINY_GSM_USE_WIFI false | ||||
|  | ||||
| // set GSM PIN, if any | ||||
| #define GSM_PIN "" | ||||
|  | ||||
| // Your GPRS credentials | ||||
| // Leave empty, if missing user or pass | ||||
| @@ -81,7 +88,15 @@ void setup() { | ||||
|   // Set console baud rate | ||||
|   SerialMon.begin(115200); | ||||
|   delay(10); | ||||
|   SerialMon.println(F("Wait...")); | ||||
|  | ||||
|   // Set your reset, enable, power pins here | ||||
|   pinMode(20, OUTPUT); | ||||
|   digitalWrite(20, HIGH); | ||||
|  | ||||
|   pinMode(23, OUTPUT); | ||||
|   digitalWrite(23, HIGH); | ||||
|  | ||||
|   SerialMon.println("Wait..."); | ||||
|  | ||||
|   // Set GSM module baud rate | ||||
|   SerialAT.begin(115200); | ||||
| @@ -89,11 +104,11 @@ void setup() { | ||||
|  | ||||
|   // Restart takes quite some time | ||||
|   // To skip it, call init() instead of restart() | ||||
|   SerialMon.println(F("Initializing modem...")); | ||||
|   SerialMon.println("Initializing modem..."); | ||||
|   modem.restart(); | ||||
|  | ||||
|   String modemInfo = modem.getModemInfo(); | ||||
|   SerialMon.print(F("Modem: ")); | ||||
|   SerialMon.print("Modem: "); | ||||
|   SerialMon.println(modemInfo); | ||||
|  | ||||
|   // Unlock your SIM card with a PIN | ||||
| @@ -107,17 +122,17 @@ void setup() { | ||||
|  | ||||
| void loop() { | ||||
|  | ||||
|   if (modem.hasWifi()) { | ||||
|     SerialMon.print(F("Setting SSID/password...")); | ||||
|     if (!modem.networkConnect(wifiSSID, wifiPass)) { | ||||
|       SerialMon.println(" fail"); | ||||
|       delay(10000); | ||||
|       return; | ||||
|     } | ||||
|     SerialMon.println(" OK"); | ||||
| #if TINY_GSM_USE_WIFI | ||||
|   SerialMon.print(F("Setting SSID/password...")); | ||||
|   if (!modem.networkConnect(wifiSSID, wifiPass)) { | ||||
|     SerialMon.println(" fail"); | ||||
|     delay(10000); | ||||
|     return; | ||||
|   } | ||||
|   SerialMon.println(" OK"); | ||||
| #endif | ||||
|  | ||||
|   SerialMon.print(F("Waiting for network...")); | ||||
|   SerialMon.print("Waiting for network..."); | ||||
|   if (!modem.waitForNetwork()) { | ||||
|     SerialMon.println(" fail"); | ||||
|     delay(10000); | ||||
| @@ -125,7 +140,11 @@ void loop() { | ||||
|   } | ||||
|   SerialMon.println(" OK"); | ||||
|  | ||||
|   if (modem.hasGPRS()) { | ||||
|   if (modem.isNetworkConnected()) { | ||||
|     SerialMon.print("Network connected"); | ||||
|   } | ||||
|  | ||||
| #if TINY_GSM_USE_GPRS | ||||
|     SerialMon.print(F("Connecting to ")); | ||||
|     SerialMon.print(apn); | ||||
|     if (!modem.gprsConnect(apn, gprsUser, gprsPass)) { | ||||
| @@ -134,7 +153,7 @@ void loop() { | ||||
|       return; | ||||
|     } | ||||
|     SerialMon.println(" OK"); | ||||
|   } | ||||
| #endif | ||||
|  | ||||
|   SerialMon.print(F("Performing HTTPS GET request... ")); | ||||
|   http.connectionKeepAlive(); // Currently, this is needed for HTTPS | ||||
| @@ -181,8 +200,14 @@ void loop() { | ||||
|   http.stop(); | ||||
|   SerialMon.println(F("Server disconnected")); | ||||
|  | ||||
|   modem.gprsDisconnect(); | ||||
|   SerialMon.println(F("GPRS disconnected")); | ||||
| #if TINY_GSM_USE_WIFI | ||||
|     modem.networkDisconnect(); | ||||
|     SerialMon.println(F("WiFi disconnected")); | ||||
| #endif | ||||
| #if TINY_GSM_USE_GPRS | ||||
|     modem.gprsDisconnect(); | ||||
|     SerialMon.println(F("GPRS disconnected")); | ||||
| #endif | ||||
|  | ||||
|   // Do nothing forevermore | ||||
|   while (true) { | ||||
|   | ||||
| @@ -29,11 +29,12 @@ | ||||
|  | ||||
| // Select your modem: | ||||
| #define TINY_GSM_MODEM_SIM800 | ||||
| // #define TINY_GSM_MODEM_SIM900 | ||||
| // #define TINY_GSM_MODEM_SIM808 | ||||
| // #define TINY_GSM_MODEM_SIM868 | ||||
| // #define TINY_GSM_MODEM_SIM900 | ||||
| // #define TINY_GSM_MODEM_SIM7000 | ||||
| // #define TINY_GSM_MODEM_UBLOX | ||||
| // #define TINY_GSM_MODEM_SARAR4 | ||||
| // #define TINY_GSM_MODEM_M95 | ||||
| // #define TINY_GSM_MODEM_BG96 | ||||
| // #define TINY_GSM_MODEM_A6 | ||||
| @@ -43,6 +44,7 @@ | ||||
| // #define TINY_GSM_MODEM_MC60E | ||||
| // #define TINY_GSM_MODEM_ESP8266 | ||||
| // #define TINY_GSM_MODEM_XBEE | ||||
| // #define TINY_GSM_MODEM_SEQUANS_MONARCH | ||||
|  | ||||
| #include <TinyGsmClient.h> | ||||
| #include <PubSubClient.h> | ||||
|   | ||||
| @@ -10,11 +10,12 @@ | ||||
|  | ||||
| // Select your modem: | ||||
| #define TINY_GSM_MODEM_SIM800 | ||||
| // #define TINY_GSM_MODEM_SIM900 | ||||
| // #define TINY_GSM_MODEM_SIM808 | ||||
| // #define TINY_GSM_MODEM_SIM868 | ||||
| // #define TINY_GSM_MODEM_SIM900 | ||||
| // #define TINY_GSM_MODEM_SIM7000 | ||||
| // #define TINY_GSM_MODEM_UBLOX | ||||
| // #define TINY_GSM_MODEM_SARAR4 | ||||
| // #define TINY_GSM_MODEM_M95 | ||||
| // #define TINY_GSM_MODEM_BG96 | ||||
| // #define TINY_GSM_MODEM_A6 | ||||
| @@ -24,21 +25,13 @@ | ||||
| // #define TINY_GSM_MODEM_MC60E | ||||
| // #define TINY_GSM_MODEM_ESP8266 | ||||
| // #define TINY_GSM_MODEM_XBEE | ||||
| // #define TINY_GSM_MODEM_SEQUANS_MONARCH | ||||
|  | ||||
| // Increase RX buffer if needed | ||||
| //#define TINY_GSM_RX_BUFFER 512 | ||||
|  | ||||
| // See the debugging, if wanted | ||||
| //#define TINY_GSM_DEBUG Serial | ||||
| //#define LOGGING | ||||
| // #define TINY_GSM_RX_BUFFER 512 | ||||
|  | ||||
| // Add a reception delay, if needed | ||||
| //#define TINY_GSM_YIELD() { delay(1); } | ||||
|  | ||||
| #include <TinyGsmClient.h> | ||||
|  | ||||
| // Uncomment this if you want to see all AT commands | ||||
| //#define DUMP_AT_COMMANDS | ||||
| // #define TINY_GSM_YIELD() { delay(1); } | ||||
|  | ||||
| // Uncomment this if you want to use SSL | ||||
| //#define USE_SSL | ||||
| @@ -46,6 +39,7 @@ | ||||
| // Set serial for debug console (to the Serial Monitor, default speed 115200) | ||||
| #define SerialMon Serial | ||||
|  | ||||
| // Set serial for AT commands (to the module) | ||||
| // Use Hardware Serial on Mega, Leonardo, Micro | ||||
| #define SerialAT Serial1 | ||||
|  | ||||
| @@ -53,6 +47,21 @@ | ||||
| //#include <SoftwareSerial.h> | ||||
| //SoftwareSerial SerialAT(2, 3); // RX, TX | ||||
|  | ||||
| // See all AT commands, if wanted | ||||
| //#define DUMP_AT_COMMANDS | ||||
|  | ||||
| // See the debugging, if wanted | ||||
| // #define TINY_GSM_DEBUG SerialMon | ||||
|  | ||||
| // Range to attempt to autobaud | ||||
| #define GSM_AUTOBAUD_MIN 9600 | ||||
| #define GSM_AUTOBAUD_MAX 38400 | ||||
|  | ||||
| #define TINY_GSM_USE_GPRS true | ||||
| #define TINY_GSM_USE_WIFI false | ||||
|  | ||||
| // set GSM PIN, if any | ||||
| #define GSM_PIN "" | ||||
|  | ||||
| // Your GPRS credentials | ||||
| // Leave empty, if missing user or pass | ||||
| @@ -66,6 +75,8 @@ const char wifiPass[] = "SSIDpw"; | ||||
| const char server[] = "vsh.pp.ua"; | ||||
| const char resource[] = "/TinyGSM/logo.txt"; | ||||
|  | ||||
| #include <TinyGsmClient.h> | ||||
|  | ||||
| #ifdef DUMP_AT_COMMANDS | ||||
|   #include <StreamDebugger.h> | ||||
|   StreamDebugger debugger(SerialAT, SerialMon); | ||||
| @@ -86,22 +97,29 @@ void setup() { | ||||
|   // Set console baud rate | ||||
|   SerialMon.begin(115200); | ||||
|   delay(10); | ||||
|   SerialMon.println(F("Wait...")); | ||||
|  | ||||
|   // Set your reset, enable, power pins here | ||||
|   pinMode(20, OUTPUT); | ||||
|   digitalWrite(20, HIGH); | ||||
|  | ||||
|   pinMode(23, OUTPUT); | ||||
|   digitalWrite(23, LOW); | ||||
|   digitalWrite(23, HIGH); | ||||
|  | ||||
|   SerialMon.println("Wait..."); | ||||
|  | ||||
|   // Set GSM module baud rate | ||||
|   // TinyGsmAutoBaud(SerialAT,GSM_AUTOBAUD_MIN,GSM_AUTOBAUD_MAX); | ||||
|   SerialAT.begin(9600); | ||||
|   delay(3000); | ||||
|  | ||||
|   // Restart takes quite some time | ||||
|   // To skip it, call init() instead of restart() | ||||
|   SerialMon.println(F("Initializing modem...")); | ||||
|   SerialMon.println("Initializing modem..."); | ||||
|   modem.restart(); | ||||
|   // modem.init(); | ||||
|  | ||||
|   String modemInfo = modem.getModemInfo(); | ||||
|   SerialMon.print(F("Modem: ")); | ||||
|   SerialMon.print("Modem: "); | ||||
|   SerialMon.println(modemInfo); | ||||
|  | ||||
|   // Unlock your SIM card with a PIN | ||||
| @@ -110,46 +128,41 @@ void setup() { | ||||
|  | ||||
| void loop() { | ||||
|  | ||||
|   if (modem.hasWifi()) { | ||||
|     SerialMon.print(F("Setting SSID/password...")); | ||||
|     if (!modem.networkConnect(wifiSSID, wifiPass)) { | ||||
|       SerialMon.println(" fail"); | ||||
|       delay(10000); | ||||
|       return; | ||||
|     } | ||||
|     SerialMon.println(" OK"); | ||||
|   } | ||||
|   else if (modem.getModemName().indexOf("XBee") >= 0) { | ||||
|     SerialMon.print(F("Setting APN")); | ||||
|     if (!modem.gprsConnect(apn)) { | ||||
|       SerialMon.println(" fail"); | ||||
|       delay(10000); | ||||
|       return; | ||||
|     } | ||||
|     SerialMon.println(" OK"); | ||||
| #if TINY_GSM_USE_WIFI | ||||
|   SerialMon.print(F("Setting SSID/password...")); | ||||
|   if (!modem.networkConnect(wifiSSID, wifiPass)) { | ||||
|     SerialMon.println(" fail"); | ||||
|     delay(10000); | ||||
|     return; | ||||
|   } | ||||
|   SerialMon.println(" OK"); | ||||
| #endif | ||||
|  | ||||
|   SerialMon.print(F("Waiting for network...")); | ||||
|   if (!modem.waitForNetwork()) { | ||||
|   SerialMon.print("Waiting for network..."); | ||||
|   if (!modem.waitForNetwork(240000L)) { | ||||
|     SerialMon.println(" fail"); | ||||
|     delay(10000); | ||||
|     return; | ||||
|   } | ||||
|   SerialMon.println(" OK"); | ||||
|  | ||||
|   if (modem.hasGPRS()) { | ||||
|   if (modem.isNetworkConnected()) { | ||||
|     SerialMon.println("Network connected"); | ||||
|   } | ||||
|  | ||||
| #if TINY_GSM_USE_GPRS | ||||
|     SerialMon.print(F("Connecting to ")); | ||||
|     SerialMon.print(apn); | ||||
|     SerialMon.println(apn); | ||||
|     if (!modem.gprsConnect(apn, gprsUser, gprsPass)) { | ||||
|       SerialMon.println(" fail"); | ||||
|       delay(10000); | ||||
|       return; | ||||
|     } | ||||
|     SerialMon.println(" OK"); | ||||
|   } | ||||
| #endif | ||||
|  | ||||
|   SerialMon.print(F("Connecting to ")); | ||||
|   SerialMon.print(server); | ||||
|   SerialMon.print("Connecting to "); | ||||
|   SerialMon.println(server); | ||||
|   if (!client.connect(server, port)) { | ||||
|     SerialMon.println(" fail"); | ||||
|     delay(10000); | ||||
| @@ -158,6 +171,7 @@ void loop() { | ||||
|   SerialMon.println(" OK"); | ||||
|  | ||||
|   // Make a HTTP GET request: | ||||
|   SerialMon.println("Performing HTTP GET request..."); | ||||
|   client.print(String("GET ") + resource + " HTTP/1.0\r\n"); | ||||
|   client.print(String("Host: ") + server + "\r\n"); | ||||
|   client.print("Connection: close\r\n\r\n"); | ||||
| @@ -178,14 +192,14 @@ void loop() { | ||||
|   client.stop(); | ||||
|   SerialMon.println(F("Server disconnected")); | ||||
|  | ||||
|   if (modem.hasWifi()) { | ||||
| #if TINY_GSM_USE_WIFI | ||||
|     modem.networkDisconnect(); | ||||
|     SerialMon.println(F("WiFi disconnected")); | ||||
|   } | ||||
|   else { | ||||
| #endif | ||||
| #if TINY_GSM_USE_GPRS | ||||
|     modem.gprsDisconnect(); | ||||
|     SerialMon.println(F("GPRS disconnected")); | ||||
|   } | ||||
| #endif | ||||
|  | ||||
|   // Do nothing forevermore | ||||
|   while (true) { | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								extras/AT Command Manuals/Sequans Monarch AT Commands Manual.pdf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								extras/AT Command Manuals/Sequans Monarch AT Commands Manual.pdf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "TinyGSM", | ||||
|   "version": "0.6.2", | ||||
|   "version": "0.7.4", | ||||
|   "description": "A small Arduino library for GPRS modules, that just works. Includes examples for Blynk, MQTT, File Download, and Web Client. Supports many GSM, LTE, and WiFi modules with AT command interfaces.", | ||||
|   "keywords": "GSM, AT commands, AT, SIM800, SIM900, A6, A7, M590, ESP8266, SIM7000, SIM800A, SIM800C, SIM800L, SIM800H, SIM808, SIM868, SIM900A, SIM900D, SIM908, SIM968, M95, MC60, MC60E, BG96, ublox, Quectel, SIMCOM, AI Thinker, LTE, LTE-M", | ||||
|   "authors": | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| name=TinyGSM | ||||
| version=0.6.2 | ||||
| version=0.7.4 | ||||
| author=Volodymyr Shymanskyy | ||||
| maintainer=Volodymyr Shymanskyy | ||||
| sentence=A small Arduino library for GPRS modules, that just works. | ||||
|   | ||||
| @@ -48,6 +48,14 @@ | ||||
|   typedef TinyGsmUBLOX::GsmClient TinyGsmClient; | ||||
|   typedef TinyGsmUBLOX::GsmClientSecure TinyGsmClientSecure; | ||||
|  | ||||
| #elif defined(TINY_GSM_MODEM_SARAR4) | ||||
|   #define TINY_GSM_MODEM_HAS_GPRS | ||||
|   #define TINY_GSM_MODEM_HAS_SSL | ||||
|   #include <TinyGsmClientSaraR4.h> | ||||
|   typedef TinyGsmSaraR4 TinyGsm; | ||||
|   typedef TinyGsmSaraR4::GsmClient TinyGsmClient; | ||||
|   typedef TinyGsmSaraR4::GsmClientSecure TinyGsmClientSecure; | ||||
|  | ||||
| #elif defined(TINY_GSM_MODEM_M95) | ||||
|   #define TINY_GSM_MODEM_HAS_GPRS | ||||
|   #include <TinyGsmClientM95.h> | ||||
| @@ -96,6 +104,15 @@ | ||||
|   typedef TinyGsmXBee::GsmClient TinyGsmClient; | ||||
|   typedef TinyGsmXBee::GsmClientSecure TinyGsmClientSecure; | ||||
|  | ||||
| #elif defined(TINY_GSM_MODEM_SEQUANS_MONARCH) | ||||
|   #define TINY_GSM_MODEM_HAS_GPRS | ||||
|   #define TINY_GSM_MODEM_HAS_SSL | ||||
|   #include <TinyGsmClientSequansMonarch.h> | ||||
|   typedef TinyGsmSequansMonarch TinyGsm; | ||||
|   typedef TinyGsmSequansMonarch::GsmClient TinyGsmClient; | ||||
|   typedef TinyGsmSequansMonarch::GsmClientSecure TinyGsmClientSecure; | ||||
|  | ||||
|  | ||||
| #else | ||||
|   #error "Please define GSM modem model" | ||||
| #endif | ||||
|   | ||||
| @@ -12,6 +12,10 @@ | ||||
|  | ||||
| //#define TINY_GSM_DEBUG Serial | ||||
|  | ||||
| #if !defined(TINY_GSM_RX_BUFFER) | ||||
|   #define TINY_GSM_RX_BUFFER 256 | ||||
| #endif | ||||
|  | ||||
| #define TINY_GSM_MUX_COUNT 8 | ||||
|  | ||||
| #include <TinyGsmCommon.h> | ||||
| @@ -36,7 +40,7 @@ enum RegStatus { | ||||
| }; | ||||
|  | ||||
|  | ||||
| class TinyGsmA6 : public TinyGsmModem | ||||
| class TinyGsmA6 | ||||
| { | ||||
|  | ||||
| public: | ||||
| @@ -58,40 +62,24 @@ public: | ||||
|     this->mux = -1; | ||||
|     sock_connected = false; | ||||
|  | ||||
|     // at->sockets[mux] = this; | ||||
|     //  ^^ TODO: attach the socket here at init?  Or later at connect? | ||||
|     // Currently done inconsistently between modems | ||||
|  | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
| public: | ||||
|   virtual int connect(const char *host, uint16_t port) { | ||||
|   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||
|     stop(); | ||||
|     TINY_GSM_YIELD(); | ||||
|     rx.clear(); | ||||
|     uint8_t newMux = -1; | ||||
|     sock_connected = at->modemConnect(host, port, &newMux); | ||||
|     sock_connected = at->modemConnect(host, port, &newMux, timeout_s); | ||||
|     if (sock_connected) { | ||||
|       mux = newMux; | ||||
|       at->sockets[mux] = this; | ||||
|     // ^^ TODO: attach the socet after attempting connection or above at init? | ||||
|     // Currently done inconsistently between modems | ||||
|     } | ||||
|     return sock_connected; | ||||
|   } | ||||
|  | ||||
|   virtual int connect(IPAddress ip, uint16_t port) { | ||||
|     String host; host.reserve(16); | ||||
|     host += ip[0]; | ||||
|     host += "."; | ||||
|     host += ip[1]; | ||||
|     host += "."; | ||||
|     host += ip[2]; | ||||
|     host += "."; | ||||
|     host += ip[3]; | ||||
|     return connect(host.c_str(), port); | ||||
|   } | ||||
| TINY_GSM_CLIENT_CONNECT_OVERLOADS() | ||||
|  | ||||
|   virtual void stop() { | ||||
|     TINY_GSM_YIELD(); | ||||
| @@ -101,67 +89,13 @@ public: | ||||
|     rx.clear(); | ||||
|   } | ||||
|  | ||||
|   virtual size_t write(const uint8_t *buf, size_t size) { | ||||
|     TINY_GSM_YIELD(); | ||||
|     //at->maintain(); | ||||
|     return at->modemSend(buf, size, mux); | ||||
|   } | ||||
| TINY_GSM_CLIENT_WRITE() | ||||
|  | ||||
|   virtual size_t write(uint8_t c) { | ||||
|     return write(&c, 1); | ||||
|   } | ||||
| TINY_GSM_CLIENT_AVAILABLE_NO_MODEM_FIFO() | ||||
|  | ||||
|   virtual size_t write(const char *str) { | ||||
|     if (str == NULL) return 0; | ||||
|     return write((const uint8_t *)str, strlen(str)); | ||||
|   } | ||||
| TINY_GSM_CLIENT_READ_NO_MODEM_FIFO() | ||||
|  | ||||
|   virtual int available() { | ||||
|     TINY_GSM_YIELD(); | ||||
|     if (!rx.size() && sock_connected) { | ||||
|       at->maintain(); | ||||
|     } | ||||
|     return rx.size(); | ||||
|   } | ||||
|  | ||||
|   virtual int read(uint8_t *buf, size_t size) { | ||||
|     TINY_GSM_YIELD(); | ||||
|     size_t cnt = 0; | ||||
|     uint32_t _startMillis = millis(); | ||||
|     while (cnt < size && millis() - _startMillis < _timeout) { | ||||
|       size_t chunk = TinyGsmMin(size-cnt, rx.size()); | ||||
|       if (chunk > 0) { | ||||
|         rx.get(buf, chunk); | ||||
|         buf += chunk; | ||||
|         cnt += chunk; | ||||
|         continue; | ||||
|       } | ||||
|       // TODO: Read directly into user buffer? | ||||
|       if (!rx.size() && sock_connected) { | ||||
|         at->maintain(); | ||||
|       } | ||||
|     } | ||||
|     return cnt; | ||||
|   } | ||||
|  | ||||
|   virtual int read() { | ||||
|     uint8_t c; | ||||
|     if (read(&c, 1) == 1) { | ||||
|       return c; | ||||
|     } | ||||
|     return -1; | ||||
|   } | ||||
|  | ||||
|   virtual int peek() { return -1; } //TODO | ||||
|   virtual void flush() { at->stream.flush(); } | ||||
|  | ||||
|   virtual uint8_t connected() { | ||||
|     if (available()) { | ||||
|       return true; | ||||
|     } | ||||
|     return sock_connected; | ||||
|   } | ||||
|   virtual operator bool() { return connected(); } | ||||
| TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() | ||||
|  | ||||
|   /* | ||||
|    * Extended API | ||||
| @@ -180,7 +114,7 @@ private: | ||||
| public: | ||||
|  | ||||
|   TinyGsmA6(Stream& stream) | ||||
|     : TinyGsmModem(stream), stream(stream) | ||||
|     : stream(stream) | ||||
|   { | ||||
|     memset(sockets, 0, sizeof(sockets)); | ||||
|   } | ||||
| @@ -189,6 +123,10 @@ public: | ||||
|    * Basic functions | ||||
|    */ | ||||
|  | ||||
|    bool begin(const char* pin = NULL) { | ||||
|      return init(pin); | ||||
|    } | ||||
|  | ||||
|   bool init(const char* pin = NULL) { | ||||
|     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); | ||||
|     if (!testAT()) { | ||||
| @@ -216,22 +154,11 @@ public: | ||||
|     return "AI-Thinker A6"; | ||||
|   } | ||||
|  | ||||
|   void setBaud(unsigned long baud) { | ||||
|     sendAT(GF("+IPR="), baud); | ||||
|   } | ||||
| TINY_GSM_MODEM_SET_BAUD_IPR() | ||||
|  | ||||
|   bool testAT(unsigned long timeout = 10000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { | ||||
|       sendAT(GF("")); | ||||
|       if (waitResponse(200) == 1) return true; | ||||
|       delay(100); | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
| TINY_GSM_MODEM_TEST_AT() | ||||
|  | ||||
|   void maintain() { | ||||
|     waitResponse(10, NULL, NULL); | ||||
|   } | ||||
| TINY_GSM_MODEM_MAINTAIN_LISTEN() | ||||
|  | ||||
|   bool factoryDefault() { | ||||
|     sendAT(GF("&FZE0&W"));  // Factory + Reset + Echo Off + Write | ||||
| @@ -240,17 +167,7 @@ public: | ||||
|     return waitResponse() == 1; | ||||
|   } | ||||
|  | ||||
|   String getModemInfo() { | ||||
|     sendAT(GF("I")); | ||||
|     String res; | ||||
|     if (waitResponse(1000L, res) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     res.replace(GSM_NL "OK" GSM_NL, ""); | ||||
|     res.replace(GSM_NL, " "); | ||||
|     res.trim(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_INFO_ATI() | ||||
|  | ||||
|   bool hasSSL() { | ||||
|     return false; | ||||
| @@ -290,10 +207,7 @@ public: | ||||
|    * SIM card functions | ||||
|    */ | ||||
|  | ||||
|   bool simUnlock(const char *pin) { | ||||
|     sendAT(GF("+CPIN=\""), pin, GF("\"")); | ||||
|     return waitResponse() == 1; | ||||
|   } | ||||
| TINY_GSM_MODEM_SIM_UNLOCK_CPIN() | ||||
|  | ||||
|   String getSimCCID() { | ||||
|     sendAT(GF("+CCID")); | ||||
| @@ -306,19 +220,10 @@ public: | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   String getIMEI() { | ||||
|     sendAT(GF("+GSN")); | ||||
|     if (waitResponse(GF(GSM_NL)) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     String res = stream.readStringUntil('\n'); | ||||
|     waitResponse(); | ||||
|     res.trim(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_IMEI_GSN() | ||||
|  | ||||
|   SimStatus getSimStatus(unsigned long timeout = 10000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { | ||||
|   SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout_ms; ) { | ||||
|       sendAT(GF("+CPIN?")); | ||||
|       if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { | ||||
|         delay(1000); | ||||
| @@ -336,16 +241,7 @@ public: | ||||
|     return SIM_ERROR; | ||||
|   } | ||||
|  | ||||
|   RegStatus getRegistrationStatus() { | ||||
|     sendAT(GF("+CREG?")); | ||||
|     if (waitResponse(GF(GSM_NL "+CREG:")) != 1) { | ||||
|       return REG_UNKNOWN; | ||||
|     } | ||||
|     streamSkipUntil(','); // Skip format (0) | ||||
|     int status = stream.readStringUntil('\n').toInt(); | ||||
|     waitResponse(); | ||||
|     return (RegStatus)status; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_REGISTRATION_XREG(CREG) | ||||
|  | ||||
|   String getOperator() { | ||||
|     sendAT(GF("+COPS=3,0")); // Set format | ||||
| @@ -365,24 +261,19 @@ public: | ||||
|    * Generic network functions | ||||
|    */ | ||||
|  | ||||
|   int16_t getSignalQuality() { | ||||
|     sendAT(GF("+CSQ")); | ||||
|     if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) { | ||||
|       return 99; | ||||
|     } | ||||
|     int res = stream.readStringUntil(',').toInt(); | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_CSQ() | ||||
|  | ||||
|   bool isNetworkConnected() { | ||||
|     RegStatus s = getRegistrationStatus(); | ||||
|     return (s == REG_OK_HOME || s == REG_OK_ROAMING); | ||||
|   } | ||||
|  | ||||
| TINY_GSM_MODEM_WAIT_FOR_NETWORK() | ||||
|  | ||||
|   /* | ||||
|    * GPRS functions | ||||
|    */ | ||||
|  | ||||
|   bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { | ||||
|     gprsDisconnect(); | ||||
|  | ||||
| @@ -454,6 +345,10 @@ public: | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   IPAddress localIP() { | ||||
|     return TinyGsmIpFromString(getLocalIP()); | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    * Phone Call functions | ||||
|    */ | ||||
| @@ -587,7 +482,7 @@ public: | ||||
|   String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|  | ||||
|   /* | ||||
|    * Battery functions | ||||
|    * Battery & temperature functions | ||||
|    */ | ||||
|  | ||||
|   uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
| @@ -597,27 +492,58 @@ public: | ||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     stream.readStringUntil(','); | ||||
|     streamSkipUntil(','); // Skip battery charge status | ||||
|     // Read battery charge level | ||||
|     int res = stream.readStringUntil('\n').toInt(); | ||||
|     // Wait for final OK | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   uint8_t getBattChargeState() { | ||||
|     sendAT(GF("+CBC?")); | ||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     // Read battery charge status | ||||
|     int res = stream.readStringUntil(',').toInt(); | ||||
|     // Wait for final OK | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) { | ||||
|     sendAT(GF("+CBC?")); | ||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     chargeState = stream.readStringUntil(',').toInt(); | ||||
|     percent = stream.readStringUntil('\n').toInt(); | ||||
|     // Wait for final OK | ||||
|     waitResponse(); | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|  | ||||
|   float getTemperature() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|  | ||||
|   /* | ||||
|    * Client related functions | ||||
|    */ | ||||
|  | ||||
| protected: | ||||
|  | ||||
|   bool modemConnect(const char* host, uint16_t port, uint8_t* mux) { | ||||
|     sendAT(GF("+CIPSTART="),  GF("\"TCP"), GF("\",\""), host, GF("\","), port); | ||||
|   bool modemConnect(const char* host, uint16_t port, uint8_t* mux, int timeout_s = 75) { | ||||
|     unsigned long startMillis = millis(); | ||||
|     uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; | ||||
|  | ||||
|     if (waitResponse(75000L, GF(GSM_NL "+CIPNUM:")) != 1) { | ||||
|     sendAT(GF("+CIPSTART="),  GF("\"TCP"), GF("\",\""), host, GF("\","), port); | ||||
|     if (waitResponse(timeout_ms, GF(GSM_NL "+CIPNUM:")) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     int newMux = stream.readStringUntil('\n').toInt(); | ||||
|  | ||||
|     int rsp = waitResponse(75000L, | ||||
|     int rsp = waitResponse((timeout_ms- (millis() - startMillis)), | ||||
|                            GF("CONNECT OK" GSM_NL), | ||||
|                            GF("CONNECT FAIL" GSM_NL), | ||||
|                            GF("ALREADY CONNECT" GSM_NL)); | ||||
| @@ -655,16 +581,10 @@ public: | ||||
|    Utilities | ||||
|    */ | ||||
|  | ||||
|   template<typename... Args> | ||||
|   void sendAT(Args... cmd) { | ||||
|     streamWrite("AT", cmd..., GSM_NL); | ||||
|     stream.flush(); | ||||
|     TINY_GSM_YIELD(); | ||||
|     //DBG("### AT:", cmd...); | ||||
|   } | ||||
| TINY_GSM_MODEM_STREAM_UTILITIES() | ||||
|  | ||||
|   // TODO: Optimize this! | ||||
|   uint8_t waitResponse(uint32_t timeout, String& data, | ||||
|   uint8_t waitResponse(uint32_t timeout_ms, String& data, | ||||
|                        GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||
|   { | ||||
| @@ -708,8 +628,7 @@ public: | ||||
|             DBG("### Got: ", len, "->", sockets[mux]->rx.free()); | ||||
|           } | ||||
|           while (len--) { | ||||
|             while (!stream.available()) { TINY_GSM_YIELD(); } | ||||
|             sockets[mux]->rx.put(stream.read()); | ||||
|             TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT | ||||
|           } | ||||
|           if (len_orig > sockets[mux]->available()) { // TODO | ||||
|             DBG("### Fewer characters received than expected: ", sockets[mux]->available(), " vs ", len_orig); | ||||
| @@ -724,7 +643,7 @@ public: | ||||
|           DBG("### Closed: ", mux); | ||||
|         } | ||||
|       } | ||||
|     } while (millis() - startMillis < timeout); | ||||
|     } while (millis() - startMillis < timeout_ms); | ||||
| finish: | ||||
|     if (!index) { | ||||
|       data.trim(); | ||||
| @@ -737,12 +656,12 @@ finish: | ||||
|     return index; | ||||
|   } | ||||
|  | ||||
|   uint8_t waitResponse(uint32_t timeout, | ||||
|   uint8_t waitResponse(uint32_t timeout_ms, | ||||
|                        GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||
|   { | ||||
|     String data; | ||||
|     return waitResponse(timeout, data, r1, r2, r3, r4, r5); | ||||
|     return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5); | ||||
|   } | ||||
|  | ||||
|   uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|   | ||||
| @@ -13,6 +13,10 @@ | ||||
| //#define TINY_GSM_DEBUG Serial | ||||
| //#define TINY_GSM_USE_HEX | ||||
|  | ||||
| #if !defined(TINY_GSM_RX_BUFFER) | ||||
|   #define TINY_GSM_RX_BUFFER 64 | ||||
| #endif | ||||
|  | ||||
| #define TINY_GSM_MUX_COUNT 12 | ||||
|  | ||||
| #include <TinyGsmCommon.h> | ||||
| @@ -37,7 +41,7 @@ enum RegStatus { | ||||
| }; | ||||
|  | ||||
|  | ||||
| class TinyGsmBG96 : public TinyGsmModem | ||||
| class TinyGsmBG96 | ||||
| { | ||||
|  | ||||
| public: | ||||
| @@ -62,36 +66,20 @@ public: | ||||
|     got_data = false; | ||||
|  | ||||
|     at->sockets[mux] = this; | ||||
|     //  ^^ TODO: attach the socket here at init?  Or later at connect? | ||||
|     // Currently done inconsistently between modems | ||||
|  | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
| public: | ||||
|   virtual int connect(const char *host, uint16_t port) { | ||||
|   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||
|     stop(); | ||||
|     TINY_GSM_YIELD(); | ||||
|     rx.clear(); | ||||
|     sock_connected = at->modemConnect(host, port, mux); | ||||
|     // sock_connected = at->modemConnect(host, port, &mux); | ||||
|     // at->sockets[mux] = this; | ||||
|     // ^^ TODO: attach the socet after attempting connection or above at init? | ||||
|     // Currently done inconsistently between modems | ||||
|     sock_connected = at->modemConnect(host, port, mux, false, timeout_s); | ||||
|     return sock_connected; | ||||
|   } | ||||
|  | ||||
|   virtual int connect(IPAddress ip, uint16_t port) { | ||||
|     String host; host.reserve(16); | ||||
|     host += ip[0]; | ||||
|     host += "."; | ||||
|     host += ip[1]; | ||||
|     host += "."; | ||||
|     host += ip[2]; | ||||
|     host += "."; | ||||
|     host += ip[3]; | ||||
|     return connect(host.c_str(), port); | ||||
|   } | ||||
| TINY_GSM_CLIENT_CONNECT_OVERLOADS() | ||||
|  | ||||
|   virtual void stop() { | ||||
|     TINY_GSM_YIELD(); | ||||
| @@ -112,70 +100,13 @@ public: | ||||
|     at->waitResponse(); | ||||
|   } | ||||
|  | ||||
|   virtual size_t write(const uint8_t *buf, size_t size) { | ||||
|     TINY_GSM_YIELD(); | ||||
|     at->maintain(); | ||||
|     return at->modemSend(buf, size, mux); | ||||
|   } | ||||
| TINY_GSM_CLIENT_WRITE() | ||||
|  | ||||
|   virtual size_t write(uint8_t c) { | ||||
|     return write(&c, 1); | ||||
|   } | ||||
| TINY_GSM_CLIENT_AVAILABLE_NO_BUFFER_CHECK() | ||||
|  | ||||
|   virtual size_t write(const char *str) { | ||||
|     if (str == NULL) return 0; | ||||
|     return write((const uint8_t *)str, strlen(str)); | ||||
|   } | ||||
| TINY_GSM_CLIENT_READ_NO_BUFFER_CHECK() | ||||
|  | ||||
|   virtual int available() { | ||||
|     TINY_GSM_YIELD(); | ||||
|     if (!rx.size()) { | ||||
|       at->maintain(); | ||||
|     } | ||||
|     return rx.size() + sock_available; | ||||
|   } | ||||
|  | ||||
|   virtual int read(uint8_t *buf, size_t size) { | ||||
|     TINY_GSM_YIELD(); | ||||
|     at->maintain(); | ||||
|     size_t cnt = 0; | ||||
|     while (cnt < size) { | ||||
|       size_t chunk = TinyGsmMin(size-cnt, rx.size()); | ||||
|       if (chunk > 0) { | ||||
|         rx.get(buf, chunk); | ||||
|         buf += chunk; | ||||
|         cnt += chunk; | ||||
|         continue; | ||||
|       } | ||||
|       // TODO: Read directly into user buffer? | ||||
|       at->maintain(); | ||||
|       if (sock_available > 0) { | ||||
|         at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux); | ||||
|       } else { | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|     return cnt; | ||||
|   } | ||||
|  | ||||
|   virtual int read() { | ||||
|     uint8_t c; | ||||
|     if (read(&c, 1) == 1) { | ||||
|       return c; | ||||
|     } | ||||
|     return -1; | ||||
|   } | ||||
|  | ||||
|   virtual int peek() { return -1; } //TODO | ||||
|   virtual void flush() { at->stream.flush(); } | ||||
|  | ||||
|   virtual uint8_t connected() { | ||||
|     if (available()) { | ||||
|       return true; | ||||
|     } | ||||
|     return sock_connected; | ||||
|   } | ||||
|   virtual operator bool() { return connected(); } | ||||
| TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() | ||||
|  | ||||
|   /* | ||||
|    * Extended API | ||||
| @@ -203,11 +134,11 @@ private: | ||||
| //   {} | ||||
| // | ||||
| // public: | ||||
| //   virtual int connect(const char *host, uint16_t port) { | ||||
| //   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||
| //     stop(); | ||||
| //     TINY_GSM_YIELD(); | ||||
| //     rx.clear(); | ||||
| //     sock_connected = at->modemConnect(host, port, mux, true); | ||||
| //     sock_connected = at->modemConnect(host, port, mux, true, timeout_s); | ||||
| //     return sock_connected; | ||||
| //   } | ||||
| // }; | ||||
| @@ -216,7 +147,7 @@ private: | ||||
| public: | ||||
|  | ||||
|   TinyGsmBG96(Stream& stream) | ||||
|     : TinyGsmModem(stream), stream(stream) | ||||
|     : stream(stream) | ||||
|   { | ||||
|     memset(sockets, 0, sizeof(sockets)); | ||||
|   } | ||||
| @@ -225,6 +156,10 @@ public: | ||||
|    * Basic functions | ||||
|    */ | ||||
|  | ||||
|   bool begin(const char* pin = NULL) { | ||||
|     return init(pin); | ||||
|   } | ||||
|  | ||||
|   bool init(const char* pin = NULL) { | ||||
|     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); | ||||
|     if (!testAT()) { | ||||
| @@ -243,31 +178,11 @@ public: | ||||
|     return "Quectel BG96"; | ||||
|   } | ||||
|  | ||||
|   void setBaud(unsigned long baud) { | ||||
|     sendAT(GF("+IPR="), baud); | ||||
|   } | ||||
| TINY_GSM_MODEM_SET_BAUD_IPR() | ||||
|  | ||||
|   bool testAT(unsigned long timeout = 10000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { | ||||
|       sendAT(GF("")); | ||||
|       if (waitResponse(200) == 1) return true; | ||||
|       delay(100); | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
| TINY_GSM_MODEM_TEST_AT() | ||||
|  | ||||
|   void maintain() { | ||||
|     for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) { | ||||
|       GsmClient* sock = sockets[mux]; | ||||
|       if (sock && sock->got_data) { | ||||
|         sock->got_data = false; | ||||
|         sock->sock_available = modemGetAvailable(mux); | ||||
|       } | ||||
|     } | ||||
|     while (stream.available()) { | ||||
|       waitResponse(10, NULL, NULL); | ||||
|     } | ||||
|   } | ||||
| TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS() | ||||
|  | ||||
|   bool factoryDefault() { | ||||
|     sendAT(GF("&FZE0&W"));  // Factory + Reset + Echo Off + Write | ||||
| @@ -278,17 +193,7 @@ public: | ||||
|     return waitResponse() == 1; | ||||
|   } | ||||
|  | ||||
|   String getModemInfo() { | ||||
|     sendAT(GF("I")); | ||||
|     String res; | ||||
|     if (waitResponse(1000L, res) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     res.replace(GSM_NL "OK" GSM_NL, ""); | ||||
|     res.replace(GSM_NL, " "); | ||||
|     res.trim(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_INFO_ATI() | ||||
|  | ||||
|   bool hasSSL() { | ||||
|     return false;  // TODO: For now | ||||
| @@ -337,14 +242,11 @@ public: | ||||
|    * SIM card functions | ||||
|    */ | ||||
|  | ||||
|   bool simUnlock(const char *pin) { | ||||
|     sendAT(GF("+CPIN=\""), pin, GF("\"")); | ||||
|     return waitResponse() == 1; | ||||
|   } | ||||
| TINY_GSM_MODEM_SIM_UNLOCK_CPIN() | ||||
|  | ||||
|   String getSimCCID() { | ||||
|     sendAT(GF("+ICCID")); | ||||
|     if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) { | ||||
|     sendAT(GF("+QCCID")); | ||||
|     if (waitResponse(GF(GSM_NL "+QCCID:")) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     String res = stream.readStringUntil('\n'); | ||||
| @@ -353,19 +255,10 @@ public: | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   String getIMEI() { | ||||
|     sendAT(GF("+GSN")); | ||||
|     if (waitResponse(GF(GSM_NL)) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     String res = stream.readStringUntil('\n'); | ||||
|     waitResponse(); | ||||
|     res.trim(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_IMEI_GSN() | ||||
|  | ||||
|   SimStatus getSimStatus(unsigned long timeout = 10000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { | ||||
|   SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout_ms; ) { | ||||
|       sendAT(GF("+CPIN?")); | ||||
|       if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { | ||||
|         delay(1000); | ||||
| @@ -383,50 +276,27 @@ public: | ||||
|     return SIM_ERROR; | ||||
|   } | ||||
|  | ||||
|   RegStatus getRegistrationStatus() { | ||||
|     sendAT(GF("+CREG?")); | ||||
|     if (waitResponse(GF(GSM_NL "+CREG:")) != 1) { | ||||
|       return REG_UNKNOWN; | ||||
|     } | ||||
|     streamSkipUntil(','); // Skip format (0) | ||||
|     int status = stream.readStringUntil('\n').toInt(); | ||||
|     waitResponse(); | ||||
|     return (RegStatus)status; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_REGISTRATION_XREG(CREG) | ||||
|  | ||||
|   String getOperator() { | ||||
|     sendAT(GF("+COPS?")); | ||||
|     if (waitResponse(GF(GSM_NL "+COPS:")) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     streamSkipUntil('"'); // Skip mode and format | ||||
|     String res = stream.readStringUntil('"'); | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_OPERATOR_COPS() | ||||
|  | ||||
|   /* | ||||
|    * Generic network functions | ||||
|    */ | ||||
|  | ||||
|   int16_t getSignalQuality() { | ||||
|     sendAT(GF("+CSQ")); | ||||
|     if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) { | ||||
|       return 99; | ||||
|     } | ||||
|     int res = stream.readStringUntil(',').toInt(); | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_CSQ() | ||||
|  | ||||
|   bool isNetworkConnected() { | ||||
|     RegStatus s = getRegistrationStatus(); | ||||
|     return (s == REG_OK_HOME || s == REG_OK_ROAMING); | ||||
|   } | ||||
|  | ||||
| TINY_GSM_MODEM_WAIT_FOR_NETWORK() | ||||
|  | ||||
|   /* | ||||
|    * GPRS functions | ||||
|    */ | ||||
|  | ||||
|   bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { | ||||
|     gprsDisconnect(); | ||||
|  | ||||
| @@ -459,18 +329,7 @@ public: | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   bool isGprsConnected() { | ||||
|     sendAT(GF("+CGATT?")); | ||||
|     if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     int res = stream.readStringUntil('\n').toInt(); | ||||
|     waitResponse(); | ||||
|     if (res != 1) | ||||
|       return false; | ||||
|  | ||||
|     return localIP() != IPAddress(0,0,0,0); | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() | ||||
|  | ||||
|   /* | ||||
|    * IP Address functions | ||||
| @@ -486,6 +345,10 @@ public: | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   IPAddress localIP() { | ||||
|     return TinyGsmIpFromString(getLocalIP()); | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    * Phone Call functions | ||||
|    */ | ||||
| @@ -573,7 +436,7 @@ public: | ||||
|   String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|  | ||||
|   /* | ||||
|    * Battery functions | ||||
|    * Battery & temperature functions | ||||
|    */ | ||||
|  | ||||
|   // Use: float vBatt = modem.getBattVoltage() / 1000.0; | ||||
| @@ -582,10 +445,11 @@ public: | ||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||
|       return 0; | ||||
|     } | ||||
|     streamSkipUntil(','); // Skip | ||||
|     streamSkipUntil(','); // Skip | ||||
|  | ||||
|     streamSkipUntil(','); // Skip battery charge status | ||||
|     streamSkipUntil(','); // Skip battery charge level | ||||
|     // return voltage in mV | ||||
|     uint16_t res = stream.readStringUntil(',').toInt(); | ||||
|     // Wait for final OK | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
| @@ -595,26 +459,59 @@ public: | ||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     stream.readStringUntil(','); | ||||
|     streamSkipUntil(','); // Skip battery charge status | ||||
|     // Read battery charge level | ||||
|     int res = stream.readStringUntil(',').toInt(); | ||||
|     // Wait for final OK | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   uint8_t getBattChargeState() { | ||||
|     sendAT(GF("+CBC?")); | ||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     // Read battery charge status | ||||
|     int res = stream.readStringUntil(',').toInt(); | ||||
|     // Wait for final OK | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) { | ||||
|     sendAT(GF("+CBC?")); | ||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     chargeState = stream.readStringUntil(',').toInt(); | ||||
|     percent = stream.readStringUntil(',').toInt(); | ||||
|     milliVolts = stream.readStringUntil('\n').toInt(); | ||||
|     // Wait for final OK | ||||
|     waitResponse(); | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   float getTemperature() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|  | ||||
|   /* | ||||
|    * Client related functions | ||||
|    */ | ||||
|  | ||||
| protected: | ||||
|  | ||||
|   bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false) { | ||||
|   bool modemConnect(const char* host, uint16_t port, uint8_t mux, | ||||
|                     bool ssl = false, int timeout_s = 20) | ||||
|  { | ||||
|     int rsp; | ||||
|     uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; | ||||
|  | ||||
|     // <PDPcontextID>(1-16), <connectID>(0-11),"TCP/UDP/TCP LISTENER/UDP SERVICE", | ||||
|     // "<IP_address>/<domain_name>",<remote_port>,<local_port>,<access_mode>(0-2 0=buffer) | ||||
|     sendAT(GF("+QIOPEN=1,"), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), port, GF(",0,0")); | ||||
|     rsp = waitResponse(); | ||||
|  | ||||
|     if (waitResponse(20000L, GF(GSM_NL "+QIOPEN:")) != 1) { | ||||
|     if (waitResponse(timeout_ms, GF(GSM_NL "+QIOPEN:")) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
| @@ -638,7 +535,6 @@ protected: | ||||
|       return 0; | ||||
|     } | ||||
|     // TODO: Wait for ACK? AT+QISEND=id,0 | ||||
|     maintain();  // look for a very quick response from the remote | ||||
|     return len; | ||||
|   } | ||||
|  | ||||
| @@ -651,13 +547,10 @@ protected: | ||||
|     sockets[mux]->sock_available = len; | ||||
|  | ||||
|     for (size_t i=0; i<len; i++) { | ||||
|       while (!stream.available()) { TINY_GSM_YIELD(); } | ||||
|       char c = stream.read(); | ||||
|       sockets[mux]->rx.put(c); | ||||
|       TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT | ||||
|     } | ||||
|     waitResponse(); | ||||
|     DBG("### READ:", len, "from", mux); | ||||
|     maintain();  // Listen for a close or other URC | ||||
|     return len; | ||||
|   } | ||||
|  | ||||
| @@ -668,13 +561,12 @@ protected: | ||||
|       streamSkipUntil(','); // Skip total received | ||||
|       streamSkipUntil(','); // Skip have read | ||||
|       result = stream.readStringUntil('\n').toInt(); | ||||
|       DBG("### DATA AVAILABLE:", result, "on", mux); | ||||
|       if (result) DBG("### DATA AVAILABLE:", result, "on", mux); | ||||
|       waitResponse(); | ||||
|     } | ||||
|     if (!result) { | ||||
|       sockets[mux]->sock_connected = modemGetConnected(mux); | ||||
|     } | ||||
|     maintain();  // Listen for a close or other URC | ||||
|     return result; | ||||
|   } | ||||
|  | ||||
| @@ -693,7 +585,6 @@ protected: | ||||
|     int res = stream.readStringUntil(',').toInt(); // socket state | ||||
|  | ||||
|     waitResponse(); | ||||
|     maintain();  // Listen for a close or other URC | ||||
|  | ||||
|     // 0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing | ||||
|     return 2 == res; | ||||
| @@ -705,16 +596,10 @@ public: | ||||
|    Utilities | ||||
|    */ | ||||
|  | ||||
|   template<typename... Args> | ||||
|   void sendAT(Args... cmd) { | ||||
|     streamWrite("AT", cmd..., GSM_NL); | ||||
|     stream.flush(); | ||||
|     TINY_GSM_YIELD(); | ||||
|     //DBG("### AT:", cmd...); | ||||
|   } | ||||
| TINY_GSM_MODEM_STREAM_UTILITIES() | ||||
|  | ||||
|   // TODO: Optimize this! | ||||
|   uint8_t waitResponse(uint32_t timeout, String& data, | ||||
|   uint8_t waitResponse(uint32_t timeout_ms, String& data, | ||||
|                        GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||
|   { | ||||
| @@ -770,7 +655,7 @@ public: | ||||
|           data = ""; | ||||
|         } | ||||
|       } | ||||
|     } while (millis() - startMillis < timeout); | ||||
|     } while (millis() - startMillis < timeout_ms); | ||||
| finish: | ||||
|     if (!index) { | ||||
|       data.trim(); | ||||
| @@ -783,12 +668,12 @@ finish: | ||||
|     return index; | ||||
|   } | ||||
|  | ||||
|   uint8_t waitResponse(uint32_t timeout, | ||||
|   uint8_t waitResponse(uint32_t timeout_ms, | ||||
|                        GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||
|   { | ||||
|     String data; | ||||
|     return waitResponse(timeout, data, r1, r2, r3, r4, r5); | ||||
|     return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5); | ||||
|   } | ||||
|  | ||||
|   uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|   | ||||
| @@ -12,6 +12,10 @@ | ||||
|  | ||||
| //#define TINY_GSM_DEBUG Serial | ||||
|  | ||||
| #if !defined(TINY_GSM_RX_BUFFER) | ||||
|   #define TINY_GSM_RX_BUFFER 512 | ||||
| #endif | ||||
|  | ||||
| #define TINY_GSM_MUX_COUNT 5 | ||||
|  | ||||
| #include <TinyGsmCommon.h> | ||||
| @@ -36,7 +40,7 @@ enum RegStatus { | ||||
|  | ||||
|  | ||||
|  | ||||
| class TinyGsmESP8266 : public TinyGsmModem | ||||
| class TinyGsmESP8266 | ||||
| { | ||||
|  | ||||
| public: | ||||
| @@ -59,36 +63,20 @@ public: | ||||
|     sock_connected = false; | ||||
|  | ||||
|     at->sockets[mux] = this; | ||||
|     //  ^^ TODO: attach the socket here at init?  Or later at connect? | ||||
|     // Currently done inconsistently between modems | ||||
|  | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
| public: | ||||
|   virtual int connect(const char *host, uint16_t port) { | ||||
|   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||
|     stop(); | ||||
|     TINY_GSM_YIELD(); | ||||
|     rx.clear(); | ||||
|     sock_connected = at->modemConnect(host, port, mux); | ||||
|     // sock_connected = at->modemConnect(host, port, &mux); | ||||
|     // at->sockets[mux] = this; | ||||
|     // ^^ TODO: attach the socet after attempting connection or above at init? | ||||
|     // Currently done inconsistently between modems | ||||
|     sock_connected = at->modemConnect(host, port, mux, false, timeout_s); | ||||
|     return sock_connected; | ||||
|   } | ||||
|  | ||||
|   virtual int connect(IPAddress ip, uint16_t port) { | ||||
|     String host; host.reserve(16); | ||||
|     host += ip[0]; | ||||
|     host += "."; | ||||
|     host += ip[1]; | ||||
|     host += "."; | ||||
|     host += ip[2]; | ||||
|     host += "."; | ||||
|     host += ip[3]; | ||||
|     return connect(host.c_str(), port); | ||||
|   } | ||||
| TINY_GSM_CLIENT_CONNECT_OVERLOADS() | ||||
|  | ||||
|   virtual void stop() { | ||||
|     TINY_GSM_YIELD(); | ||||
| @@ -98,67 +86,13 @@ public: | ||||
|     rx.clear(); | ||||
|   } | ||||
|  | ||||
|   virtual size_t write(const uint8_t *buf, size_t size) { | ||||
|     TINY_GSM_YIELD(); | ||||
|     //at->maintain(); | ||||
|     return at->modemSend(buf, size, mux); | ||||
|   } | ||||
| TINY_GSM_CLIENT_WRITE() | ||||
|  | ||||
|   virtual size_t write(uint8_t c) { | ||||
|     return write(&c, 1); | ||||
|   } | ||||
| TINY_GSM_CLIENT_AVAILABLE_NO_MODEM_FIFO() | ||||
|  | ||||
|   virtual size_t write(const char *str) { | ||||
|     if (str == NULL) return 0; | ||||
|     return write((const uint8_t *)str, strlen(str)); | ||||
|   } | ||||
| TINY_GSM_CLIENT_READ_NO_MODEM_FIFO() | ||||
|  | ||||
|   virtual int available() { | ||||
|     TINY_GSM_YIELD(); | ||||
|     if (!rx.size() && sock_connected) { | ||||
|       at->maintain(); | ||||
|     } | ||||
|     return rx.size(); | ||||
|   } | ||||
|  | ||||
|   virtual int read(uint8_t *buf, size_t size) { | ||||
|     TINY_GSM_YIELD(); | ||||
|     size_t cnt = 0; | ||||
|     uint32_t _startMillis = millis(); | ||||
|     while (cnt < size && millis() - _startMillis < _timeout) { | ||||
|       size_t chunk = TinyGsmMin(size-cnt, rx.size()); | ||||
|       if (chunk > 0) { | ||||
|         rx.get(buf, chunk); | ||||
|         buf += chunk; | ||||
|         cnt += chunk; | ||||
|         continue; | ||||
|       } | ||||
|       // TODO: Read directly into user buffer? | ||||
|       if (!rx.size() && sock_connected) { | ||||
|         at->maintain(); | ||||
|       } | ||||
|     } | ||||
|     return cnt; | ||||
|   } | ||||
|  | ||||
|   virtual int read() { | ||||
|     uint8_t c; | ||||
|     if (read(&c, 1) == 1) { | ||||
|       return c; | ||||
|     } | ||||
|     return -1; | ||||
|   } | ||||
|  | ||||
|   virtual int peek() { return -1; } //TODO | ||||
|   virtual void flush() { at->stream.flush(); } | ||||
|  | ||||
|   virtual uint8_t connected() { | ||||
|     if (available()) { | ||||
|       return true; | ||||
|     } | ||||
|     return sock_connected; | ||||
|   } | ||||
|   virtual operator bool() { return connected(); } | ||||
| TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() | ||||
|  | ||||
|   /* | ||||
|    * Extended API | ||||
| @@ -184,15 +118,11 @@ public: | ||||
|   {} | ||||
|  | ||||
| public: | ||||
|   virtual int connect(const char *host, uint16_t port) { | ||||
|   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||
|     stop(); | ||||
|     TINY_GSM_YIELD(); | ||||
|     rx.clear(); | ||||
|     sock_connected = at->modemConnect(host, port, mux, true); | ||||
|     // sock_connected = at->modemConnect(host, port, &mux); | ||||
|     // at->sockets[mux] = this; | ||||
|     // ^^ TODO: attach the socet after attempting connection or above at init? | ||||
|     // Currently done inconsistently between modems | ||||
|     sock_connected = at->modemConnect(host, port, mux, true, timeout_s); | ||||
|     return sock_connected; | ||||
|   } | ||||
| }; | ||||
| @@ -201,7 +131,7 @@ public: | ||||
| public: | ||||
|  | ||||
|   TinyGsmESP8266(Stream& stream) | ||||
|     : TinyGsmModem(stream), stream(stream) | ||||
|     : stream(stream) | ||||
|   { | ||||
|     memset(sockets, 0, sizeof(sockets)); | ||||
|   } | ||||
| @@ -210,6 +140,10 @@ public: | ||||
|    * Basic functions | ||||
|    */ | ||||
|  | ||||
|   bool begin(const char* pin = NULL) { | ||||
|     return init(pin); | ||||
|   } | ||||
|  | ||||
|   bool init(const char* pin = NULL) { | ||||
|     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); | ||||
|     if (!testAT()) { | ||||
| @@ -239,18 +173,9 @@ public: | ||||
|     sendAT(GF("+UART_CUR="), baud, "8,1,0,0"); | ||||
|   } | ||||
|  | ||||
|   bool testAT(unsigned long timeout = 10000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { | ||||
|       sendAT(GF("")); | ||||
|       if (waitResponse(200) == 1) return true; | ||||
|       delay(100); | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
| TINY_GSM_MODEM_TEST_AT() | ||||
|  | ||||
|   void maintain() { | ||||
|     waitResponse(10, NULL, NULL); | ||||
|   } | ||||
| TINY_GSM_MODEM_MAINTAIN_LISTEN() | ||||
|  | ||||
|   bool factoryDefault() { | ||||
|     sendAT(GF("+RESTORE")); | ||||
| @@ -345,8 +270,8 @@ public: | ||||
|     return (s == REG_OK_IP || s == REG_OK_TCP); | ||||
|   } | ||||
|  | ||||
|   bool waitForNetwork(unsigned long timeout = 60000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { | ||||
|   bool waitForNetwork(unsigned long timeout_ms = 60000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout_ms; ) { | ||||
|       sendAT(GF("+CIPSTATUS")); | ||||
|       int res1 = waitResponse(3000, GF("busy p..."), GF("STATUS:")); | ||||
|       if (res1 == 2) { | ||||
| @@ -364,6 +289,7 @@ public: | ||||
|   /* | ||||
|    * WiFi functions | ||||
|    */ | ||||
|  | ||||
|   bool networkConnect(const char* ssid, const char* pwd) { | ||||
|     sendAT(GF("+CWJAP_CUR=\""), ssid, GF("\",\""), pwd, GF("\"")); | ||||
|     if (waitResponse(30000L, GFP(GSM_OK), GF(GSM_NL "FAIL" GSM_NL)) != 1) { | ||||
| @@ -395,20 +321,39 @@ public: | ||||
|     return res2; | ||||
|   } | ||||
|  | ||||
|   IPAddress localIP() { | ||||
|     return TinyGsmIpFromString(getLocalIP()); | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    * Battery & temperature functions | ||||
|    */ | ||||
|  | ||||
|   // Use: float vBatt = modem.getBattVoltage() / 1000.0; | ||||
|   uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|   int8_t getBattPercent() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|   uint8_t getBattChargeState() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|   bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|   float getTemperature() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|  | ||||
|   /* | ||||
|    * Client related functions | ||||
|    */ | ||||
|  | ||||
| protected: | ||||
|  | ||||
|   bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false) { | ||||
|   bool modemConnect(const char* host, uint16_t port, uint8_t mux, | ||||
|                     bool ssl = false, int timeout_s = 75) | ||||
|  { | ||||
|     uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; | ||||
|     if (ssl) { | ||||
|       sendAT(GF("+CIPSSLSIZE=4096")); | ||||
|       waitResponse(); | ||||
|     } | ||||
|     sendAT(GF("+CIPSTART="), mux, ',', ssl ? GF("\"SSL") : GF("\"TCP"), GF("\",\""), host, GF("\","), port, GF(","), TINY_GSM_TCP_KEEP_ALIVE); | ||||
|     sendAT(GF("+CIPSTART="), mux, ',', ssl ? GF("\"SSL") : GF("\"TCP"), | ||||
|            GF("\",\""), host, GF("\","), port, GF(","), TINY_GSM_TCP_KEEP_ALIVE); | ||||
|     // TODO: Check mux | ||||
|     int rsp = waitResponse(75000L, | ||||
|     int rsp = waitResponse(timeout_ms, | ||||
|                            GFP(GSM_OK), | ||||
|                            GFP(GSM_ERROR), | ||||
|                            GF("ALREADY CONNECT")); | ||||
| @@ -440,16 +385,10 @@ public: | ||||
|    Utilities | ||||
|    */ | ||||
|  | ||||
|   template<typename... Args> | ||||
|   void sendAT(Args... cmd) { | ||||
|     streamWrite("AT", cmd..., GSM_NL); | ||||
|     stream.flush(); | ||||
|     TINY_GSM_YIELD(); | ||||
|     //DBG("### AT:", cmd...); | ||||
|   } | ||||
| TINY_GSM_MODEM_STREAM_UTILITIES() | ||||
|  | ||||
|   // TODO: Optimize this! | ||||
|   uint8_t waitResponse(uint32_t timeout, String& data, | ||||
|   uint8_t waitResponse(uint32_t timeout_ms, String& data, | ||||
|                        GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||
|   { | ||||
| @@ -493,8 +432,7 @@ public: | ||||
|             DBG("### Got: ", len, "->", sockets[mux]->rx.free()); | ||||
|           } | ||||
|           while (len--) { | ||||
|             while (!stream.available()) { TINY_GSM_YIELD(); } | ||||
|             sockets[mux]->rx.put(stream.read()); | ||||
|             TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT | ||||
|           } | ||||
|           if (len_orig > sockets[mux]->available()) { // TODO | ||||
|             DBG("### Fewer characters received than expected: ", sockets[mux]->available(), " vs ", len_orig); | ||||
| @@ -511,7 +449,7 @@ public: | ||||
|           DBG("### Closed: ", mux); | ||||
|         } | ||||
|       } | ||||
|     } while (millis() - startMillis < timeout); | ||||
|     } while (millis() - startMillis < timeout_ms); | ||||
| finish: | ||||
|     if (!index) { | ||||
|       data.trim(); | ||||
| @@ -524,12 +462,12 @@ finish: | ||||
|     return index; | ||||
|   } | ||||
|  | ||||
|   uint8_t waitResponse(uint32_t timeout, | ||||
|   uint8_t waitResponse(uint32_t timeout_ms, | ||||
|                        GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||
|   { | ||||
|     String data; | ||||
|     return waitResponse(timeout, data, r1, r2, r3, r4, r5); | ||||
|     return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5); | ||||
|   } | ||||
|  | ||||
|   uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|   | ||||
| @@ -12,6 +12,10 @@ | ||||
|  | ||||
| //#define TINY_GSM_DEBUG Serial | ||||
|  | ||||
| #if !defined(TINY_GSM_RX_BUFFER) | ||||
|   #define TINY_GSM_RX_BUFFER 256 | ||||
| #endif | ||||
|  | ||||
| #define TINY_GSM_MUX_COUNT 2 | ||||
|  | ||||
| #include <TinyGsmCommon.h> | ||||
| @@ -36,7 +40,7 @@ enum RegStatus { | ||||
| }; | ||||
|  | ||||
|  | ||||
| class TinyGsmM590 : public TinyGsmModem | ||||
| class TinyGsmM590 | ||||
| { | ||||
|  | ||||
| public: | ||||
| @@ -59,36 +63,21 @@ public: | ||||
|     sock_connected = false; | ||||
|  | ||||
|     at->sockets[mux] = this; | ||||
|     //  ^^ TODO: attach the socket here at init?  Or later at connect? | ||||
|     // Currently done inconsistently between modems | ||||
|  | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
| public: | ||||
|   virtual int connect(const char *host, uint16_t port) { | ||||
|   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||
|     stop(); | ||||
|     TINY_GSM_YIELD(); | ||||
|     rx.clear(); | ||||
|     sock_connected = at->modemConnect(host, port, mux); | ||||
|     // sock_connected = at->modemConnect(host, port, &mux); | ||||
|     // at->sockets[mux] = this; | ||||
|     // ^^ TODO: attach the socet after attempting connection or above at init? | ||||
|     // Currently done inconsistently between modems | ||||
|     sock_connected = at->modemConnect(host, port, mux, timeout_s); | ||||
|  | ||||
|     return sock_connected; | ||||
|   } | ||||
|  | ||||
|   virtual int connect(IPAddress ip, uint16_t port) { | ||||
|     String host; host.reserve(16); | ||||
|     host += ip[0]; | ||||
|     host += "."; | ||||
|     host += ip[1]; | ||||
|     host += "."; | ||||
|     host += ip[2]; | ||||
|     host += "."; | ||||
|     host += ip[3]; | ||||
|     return connect(host.c_str(), port); | ||||
|   } | ||||
| TINY_GSM_CLIENT_CONNECT_OVERLOADS() | ||||
|  | ||||
|   virtual void stop() { | ||||
|     TINY_GSM_YIELD(); | ||||
| @@ -98,67 +87,13 @@ public: | ||||
|     rx.clear(); | ||||
|   } | ||||
|  | ||||
|   virtual size_t write(const uint8_t *buf, size_t size) { | ||||
|     TINY_GSM_YIELD(); | ||||
|     //at->maintain(); | ||||
|     return at->modemSend(buf, size, mux); | ||||
|   } | ||||
| TINY_GSM_CLIENT_WRITE() | ||||
|  | ||||
|   virtual size_t write(uint8_t c) { | ||||
|     return write(&c, 1); | ||||
|   } | ||||
| TINY_GSM_CLIENT_AVAILABLE_NO_MODEM_FIFO() | ||||
|  | ||||
|   virtual size_t write(const char *str) { | ||||
|     if (str == NULL) return 0; | ||||
|     return write((const uint8_t *)str, strlen(str)); | ||||
|   } | ||||
| TINY_GSM_CLIENT_READ_NO_MODEM_FIFO() | ||||
|  | ||||
|   virtual int available() { | ||||
|     TINY_GSM_YIELD(); | ||||
|     if (!rx.size() && sock_connected) { | ||||
|       at->maintain(); | ||||
|     } | ||||
|     return rx.size(); | ||||
|   } | ||||
|  | ||||
|   virtual int read(uint8_t *buf, size_t size) { | ||||
|     TINY_GSM_YIELD(); | ||||
|     size_t cnt = 0; | ||||
|     uint32_t _startMillis = millis(); | ||||
|     while (cnt < size && millis() - _startMillis < _timeout) { | ||||
|       size_t chunk = TinyGsmMin(size-cnt, rx.size()); | ||||
|       if (chunk > 0) { | ||||
|         rx.get(buf, chunk); | ||||
|         buf += chunk; | ||||
|         cnt += chunk; | ||||
|         continue; | ||||
|       } | ||||
|       // TODO: Read directly into user buffer? | ||||
|       if (!rx.size() && sock_connected) { | ||||
|         at->maintain(); | ||||
|       } | ||||
|     } | ||||
|     return cnt; | ||||
|   } | ||||
|  | ||||
|   virtual int read() { | ||||
|     uint8_t c; | ||||
|     if (read(&c, 1) == 1) { | ||||
|       return c; | ||||
|     } | ||||
|     return -1; | ||||
|   } | ||||
|  | ||||
|   virtual int peek() { return -1; } //TODO | ||||
|   virtual void flush() { at->stream.flush(); } | ||||
|  | ||||
|   virtual uint8_t connected() { | ||||
|     if (available()) { | ||||
|       return true; | ||||
|     } | ||||
|     return sock_connected; | ||||
|   } | ||||
|   virtual operator bool() { return connected(); } | ||||
| TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() | ||||
|  | ||||
|   /* | ||||
|    * Extended API | ||||
| @@ -167,7 +102,7 @@ public: | ||||
|   String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; | ||||
|  | ||||
| private: | ||||
|   TinyGsmM590*   at; | ||||
|   TinyGsmM590*    at; | ||||
|   uint8_t         mux; | ||||
|   bool            sock_connected; | ||||
|   RxFifo          rx; | ||||
| @@ -177,7 +112,7 @@ private: | ||||
| public: | ||||
|  | ||||
|   TinyGsmM590(Stream& stream) | ||||
|     : TinyGsmModem(stream), stream(stream) | ||||
|     : stream(stream) | ||||
|   { | ||||
|     memset(sockets, 0, sizeof(sockets)); | ||||
|   } | ||||
| @@ -186,6 +121,10 @@ public: | ||||
|    * Basic functions | ||||
|    */ | ||||
|  | ||||
|   bool begin(const char* pin = NULL) { | ||||
|     return init(pin); | ||||
|   } | ||||
|  | ||||
|   bool init(const char* pin = NULL) { | ||||
|     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); | ||||
|     if (!testAT()) { | ||||
| @@ -208,24 +147,11 @@ public: | ||||
|     return "Neoway M590"; | ||||
|   } | ||||
|  | ||||
|   void setBaud(unsigned long baud) { | ||||
|     sendAT(GF("+IPR="), baud); | ||||
|   } | ||||
| TINY_GSM_MODEM_SET_BAUD_IPR() | ||||
|  | ||||
|   bool testAT(unsigned long timeout = 10000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { | ||||
|       sendAT(GF("")); | ||||
|       if (waitResponse(200) == 1) return true; | ||||
|       delay(100); | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
| TINY_GSM_MODEM_TEST_AT() | ||||
|  | ||||
|   void maintain() { | ||||
|     //while (stream.available()) { | ||||
|       waitResponse(10, NULL, NULL); | ||||
|     //} | ||||
|   } | ||||
| TINY_GSM_MODEM_MAINTAIN_LISTEN() | ||||
|  | ||||
|   bool factoryDefault() { | ||||
|     sendAT(GF("&FZE0&W"));  // Factory + Reset + Echo Off + Write | ||||
| @@ -240,17 +166,7 @@ public: | ||||
|     return waitResponse() == 1; | ||||
|   } | ||||
|  | ||||
|   String getModemInfo() { | ||||
|     sendAT(GF("I")); | ||||
|     String res; | ||||
|     if (waitResponse(1000L, res) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     res.replace(GSM_NL "OK" GSM_NL, ""); | ||||
|     res.replace(GSM_NL, " "); | ||||
|     res.trim(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_INFO_ATI() | ||||
|  | ||||
|   bool hasSSL() { | ||||
|     return false; | ||||
| @@ -297,35 +213,14 @@ public: | ||||
|    * SIM card functions | ||||
|    */ | ||||
|  | ||||
|   bool simUnlock(const char *pin) { | ||||
|     sendAT(GF("+CPIN=\""), pin, GF("\"")); | ||||
|     return waitResponse() == 1; | ||||
|   } | ||||
| TINY_GSM_MODEM_SIM_UNLOCK_CPIN() | ||||
|  | ||||
|   String getSimCCID() { | ||||
|     sendAT(GF("+CCID")); | ||||
|     if (waitResponse(GF(GSM_NL "+CCID:")) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     String res = stream.readStringUntil('\n'); | ||||
|     waitResponse(); | ||||
|     res.trim(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_SIMCCID_CCID() | ||||
|  | ||||
|   String getIMEI() { | ||||
|     sendAT(GF("+GSN")); | ||||
|     if (waitResponse(GF(GSM_NL)) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     String res = stream.readStringUntil('\n'); | ||||
|     waitResponse(); | ||||
|     res.trim(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_IMEI_GSN() | ||||
|  | ||||
|   SimStatus getSimStatus(unsigned long timeout = 10000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { | ||||
|   SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout_ms; ) { | ||||
|       sendAT(GF("+CPIN?")); | ||||
|       if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { | ||||
|         delay(1000); | ||||
| @@ -343,50 +238,27 @@ public: | ||||
|     return SIM_ERROR; | ||||
|   } | ||||
|  | ||||
|   RegStatus getRegistrationStatus() { | ||||
|     sendAT(GF("+CREG?")); | ||||
|     if (waitResponse(GF(GSM_NL "+CREG:")) != 1) { | ||||
|       return REG_UNKNOWN; | ||||
|     } | ||||
|     streamSkipUntil(','); // Skip format (0) | ||||
|     int status = stream.readStringUntil('\n').toInt(); | ||||
|     waitResponse(); | ||||
|     return (RegStatus)status; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_REGISTRATION_XREG(CREG) | ||||
|  | ||||
|   String getOperator() { | ||||
|     sendAT(GF("+COPS?")); | ||||
|     if (waitResponse(GF(GSM_NL "+COPS:")) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     streamSkipUntil('"'); // Skip mode and format | ||||
|     String res = stream.readStringUntil('"'); | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_OPERATOR_COPS() | ||||
|  | ||||
|   /* | ||||
|    * Generic network functions | ||||
|    */ | ||||
|  | ||||
|   int16_t getSignalQuality() { | ||||
|     sendAT(GF("+CSQ")); | ||||
|     if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) { | ||||
|       return 99; | ||||
|     } | ||||
|     int res = stream.readStringUntil(',').toInt(); | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_CSQ() | ||||
|  | ||||
|   bool isNetworkConnected() { | ||||
|     RegStatus s = getRegistrationStatus(); | ||||
|     return (s == REG_OK_HOME || s == REG_OK_ROAMING); | ||||
|   } | ||||
|  | ||||
| TINY_GSM_MODEM_WAIT_FOR_NETWORK() | ||||
|  | ||||
|   /* | ||||
|    * GPRS functions | ||||
|    */ | ||||
|  | ||||
|   bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { | ||||
|     gprsDisconnect(); | ||||
|  | ||||
| @@ -404,8 +276,8 @@ public: | ||||
|     sendAT(GF("+XIIC=1")); | ||||
|     waitResponse(); | ||||
|  | ||||
|     const unsigned long timeout = 60000L; | ||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { | ||||
|     const unsigned long timeout_ms = 60000L; | ||||
|     for (unsigned long start = millis(); millis() - start < timeout_ms; ) { | ||||
|       if (isGprsConnected()) { | ||||
|         //goto set_dns; // TODO | ||||
|         return true; | ||||
| @@ -456,6 +328,10 @@ public: | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   IPAddress localIP() { | ||||
|     return TinyGsmIpFromString(getLocalIP()); | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    * Phone Call functions | ||||
|    */ | ||||
| @@ -524,12 +400,14 @@ public: | ||||
|   String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|  | ||||
|   /* | ||||
|    * Battery functions | ||||
|    * Battery & temperature functions | ||||
|    */ | ||||
|  | ||||
|   uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|  | ||||
|   int8_t getBattPercent() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|   uint8_t getBattChargeState() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|   bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|   float getTemperature() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|  | ||||
|   /* | ||||
|    * Client related functions | ||||
| @@ -537,12 +415,13 @@ public: | ||||
|  | ||||
| protected: | ||||
|  | ||||
|   bool modemConnect(const char* host, uint16_t port, uint8_t mux) { | ||||
|   bool modemConnect(const char* host, uint16_t port, uint8_t mux, int timeout_s = 75) { | ||||
|     uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; | ||||
|     for (int i=0; i<3; i++) { // TODO: no need for loop? | ||||
|       String ip = dnsIpQuery(host); | ||||
|  | ||||
|       sendAT(GF("+TCPSETUP="), mux, GF(","), ip, GF(","), port); | ||||
|       int rsp = waitResponse(75000L, | ||||
|       int rsp = waitResponse(timeout_ms, | ||||
|                             GF(",OK" GSM_NL), | ||||
|                             GF(",FAIL" GSM_NL), | ||||
|                             GF("+TCPSETUP:Error" GSM_NL)); | ||||
| @@ -596,16 +475,10 @@ public: | ||||
|    Utilities | ||||
|    */ | ||||
|  | ||||
|   template<typename... Args> | ||||
|   void sendAT(Args... cmd) { | ||||
|     streamWrite("AT", cmd..., GSM_NL); | ||||
|     stream.flush(); | ||||
|     TINY_GSM_YIELD(); | ||||
|     //DBG("### AT:", cmd...); | ||||
|   } | ||||
| TINY_GSM_MODEM_STREAM_UTILITIES() | ||||
|  | ||||
|   // TODO: Optimize this! | ||||
|   uint8_t waitResponse(uint32_t timeout, String& data, | ||||
|   uint8_t waitResponse(uint32_t timeout_ms, String& data, | ||||
|                        GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||
|   { | ||||
| @@ -649,8 +522,7 @@ public: | ||||
|             DBG("### Got: ", len, "->", sockets[mux]->rx.free()); | ||||
|           } | ||||
|           while (len--) { | ||||
|             while (!stream.available()) { TINY_GSM_YIELD(); } | ||||
|             sockets[mux]->rx.put(stream.read()); | ||||
|             TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT | ||||
|           } | ||||
|           if (len_orig > sockets[mux]->available()) { // TODO | ||||
|             DBG("### Fewer characters received than expected: ", sockets[mux]->available(), " vs ", len_orig); | ||||
| @@ -666,7 +538,7 @@ public: | ||||
|           DBG("### Closed: ", mux); | ||||
|         } | ||||
|       } | ||||
|     } while (millis() - startMillis < timeout); | ||||
|     } while (millis() - startMillis < timeout_ms); | ||||
| finish: | ||||
|     if (!index) { | ||||
|       data.trim(); | ||||
| @@ -679,12 +551,12 @@ finish: | ||||
|     return index; | ||||
|   } | ||||
|  | ||||
|   uint8_t waitResponse(uint32_t timeout, | ||||
|   uint8_t waitResponse(uint32_t timeout_ms, | ||||
|                        GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||
|   { | ||||
|     String data; | ||||
|     return waitResponse(timeout, data, r1, r2, r3, r4, r5); | ||||
|     return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5); | ||||
|   } | ||||
|  | ||||
|   uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|   | ||||
| @@ -13,6 +13,10 @@ | ||||
| //#define TINY_GSM_DEBUG Serial | ||||
| //#define TINY_GSM_USE_HEX | ||||
|  | ||||
| #if !defined(TINY_GSM_RX_BUFFER) | ||||
|   #define TINY_GSM_RX_BUFFER 64 | ||||
| #endif | ||||
|  | ||||
| #define TINY_GSM_MUX_COUNT 6 | ||||
|  | ||||
| #include <TinyGsmCommon.h> | ||||
| @@ -37,7 +41,7 @@ enum RegStatus { | ||||
| }; | ||||
|  | ||||
|  | ||||
| class TinyGsmM95 : public TinyGsmModem | ||||
| class TinyGsmM95 | ||||
| { | ||||
|  | ||||
| public: | ||||
| @@ -62,36 +66,20 @@ public: | ||||
|     got_data = false; | ||||
|  | ||||
|     at->sockets[mux] = this; | ||||
|     //  ^^ TODO: attach the socket here at init?  Or later at connect? | ||||
|     // Currently done inconsistently between modems | ||||
|  | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
| public: | ||||
|   virtual int connect(const char *host, uint16_t port) { | ||||
|   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||
|     stop(); | ||||
|     TINY_GSM_YIELD(); | ||||
|     rx.clear(); | ||||
|     sock_connected = at->modemConnect(host, port, mux); | ||||
|     // sock_connected = at->modemConnect(host, port, &mux); | ||||
|     // at->sockets[mux] = this; | ||||
|     // ^^ TODO: attach the socet after attempting connection or above at init? | ||||
|     // Currently done inconsistently between modems | ||||
|     sock_connected = at->modemConnect(host, port, mux, false, timeout_s); | ||||
|     return sock_connected; | ||||
|   } | ||||
|  | ||||
|   virtual int connect(IPAddress ip, uint16_t port) { | ||||
|     String host; host.reserve(16); | ||||
|     host += ip[0]; | ||||
|     host += "."; | ||||
|     host += ip[1]; | ||||
|     host += "."; | ||||
|     host += ip[2]; | ||||
|     host += "."; | ||||
|     host += ip[3]; | ||||
|     return connect(host.c_str(), port); | ||||
|   } | ||||
| TINY_GSM_CLIENT_CONNECT_OVERLOADS() | ||||
|  | ||||
|   virtual void stop() { | ||||
|     TINY_GSM_YIELD(); | ||||
| @@ -112,70 +100,13 @@ public: | ||||
|     at->waitResponse(60000L, GF("CLOSED"), GF("CLOSE OK"), GF("ERROR")); | ||||
|   } | ||||
|  | ||||
|   virtual size_t write(const uint8_t *buf, size_t size) { | ||||
|     TINY_GSM_YIELD(); | ||||
|     at->maintain(); | ||||
|     return at->modemSend(buf, size, mux); | ||||
|   } | ||||
| TINY_GSM_CLIENT_WRITE() | ||||
|  | ||||
|   virtual size_t write(uint8_t c) { | ||||
|     return write(&c, 1); | ||||
|   } | ||||
| TINY_GSM_CLIENT_AVAILABLE_NO_BUFFER_CHECK() | ||||
|  | ||||
|   virtual size_t write(const char *str) { | ||||
|     if (str == NULL) return 0; | ||||
|     return write((const uint8_t *)str, strlen(str)); | ||||
|   } | ||||
| TINY_GSM_CLIENT_READ_NO_BUFFER_CHECK() | ||||
|  | ||||
|   virtual int available() { | ||||
|     TINY_GSM_YIELD(); | ||||
|     if (!rx.size()) { | ||||
|       at->maintain(); | ||||
|     } | ||||
|     return rx.size() + sock_available; | ||||
|   } | ||||
|  | ||||
|   virtual int read(uint8_t *buf, size_t size) { | ||||
|     TINY_GSM_YIELD(); | ||||
|     at->maintain(); | ||||
|     size_t cnt = 0; | ||||
|     while (cnt < size) { | ||||
|       size_t chunk = TinyGsmMin(size-cnt, rx.size()); | ||||
|       if (chunk > 0) { | ||||
|         rx.get(buf, chunk); | ||||
|         buf += chunk; | ||||
|         cnt += chunk; | ||||
|         continue; | ||||
|       } | ||||
|       // TODO: Read directly into user buffer? | ||||
|       at->maintain(); | ||||
|       if (sock_available > 0) { | ||||
|         at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux); | ||||
|       } else { | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|     return cnt; | ||||
|   } | ||||
|  | ||||
|   virtual int read() { | ||||
|     uint8_t c; | ||||
|     if (read(&c, 1) == 1) { | ||||
|       return c; | ||||
|     } | ||||
|     return -1; | ||||
|   } | ||||
|  | ||||
|   virtual int peek() { return -1; } //TODO | ||||
|   virtual void flush() { at->stream.flush(); } | ||||
|  | ||||
|   virtual uint8_t connected() { | ||||
|     if (available()) { | ||||
|       return true; | ||||
|     } | ||||
|     return sock_connected; | ||||
|   } | ||||
|   virtual operator bool() { return connected(); } | ||||
| TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() | ||||
|  | ||||
|   /* | ||||
|    * Extended API | ||||
| @@ -203,11 +134,11 @@ private: | ||||
| //   {} | ||||
| // | ||||
| // public: | ||||
| //   virtual int connect(const char *host, uint16_t port) { | ||||
| //   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||
| //     stop(); | ||||
| //     TINY_GSM_YIELD(); | ||||
| //     rx.clear(); | ||||
| //     sock_connected = at->modemConnect(host, port, mux, true); | ||||
| //     sock_connected = at->modemConnect(host, port, mux, true, timeout_s); | ||||
| //     return sock_connected; | ||||
| //   } | ||||
| // }; | ||||
| @@ -216,7 +147,7 @@ private: | ||||
| public: | ||||
|  | ||||
|   TinyGsmM95(Stream& stream) | ||||
|     : TinyGsmModem(stream), stream(stream) | ||||
|     : stream(stream) | ||||
|   { | ||||
|     memset(sockets, 0, sizeof(sockets)); | ||||
|   } | ||||
| @@ -225,6 +156,10 @@ public: | ||||
|    * Basic functions | ||||
|    */ | ||||
|  | ||||
|   bool begin(const char* pin = NULL) { | ||||
|     return init(pin); | ||||
|   } | ||||
|  | ||||
|   bool init(const char* pin = NULL) { | ||||
|     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); | ||||
|     if (!testAT()) { | ||||
| @@ -247,31 +182,11 @@ public: | ||||
|     return "Quectel M95"; | ||||
|   } | ||||
|  | ||||
|   void setBaud(unsigned long baud) { | ||||
|     sendAT(GF("+IPR="), baud); | ||||
|   } | ||||
| TINY_GSM_MODEM_SET_BAUD_IPR() | ||||
|  | ||||
|   bool testAT(unsigned long timeout = 10000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { | ||||
|       sendAT(GF("")); | ||||
|       if (waitResponse(200) == 1) return true; | ||||
|       delay(100); | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
| TINY_GSM_MODEM_TEST_AT() | ||||
|  | ||||
|   void maintain() { | ||||
|     for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) { | ||||
|       GsmClient* sock = sockets[mux]; | ||||
|       if (sock && sock->got_data) { | ||||
|         sock->got_data = false; | ||||
|         sock->sock_available = modemGetAvailable(mux); | ||||
|       } | ||||
|     } | ||||
|     while (stream.available()) { | ||||
|       waitResponse(10, NULL, NULL); | ||||
|     } | ||||
|   } | ||||
| TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS() | ||||
|  | ||||
|   bool factoryDefault() { | ||||
|     sendAT(GF("&FZE0&W"));  // Factory + Reset + Echo Off + Write | ||||
| @@ -282,17 +197,7 @@ public: | ||||
|     return waitResponse() == 1; | ||||
|   } | ||||
|  | ||||
|   String getModemInfo() { | ||||
|     sendAT(GF("I")); | ||||
|     String res; | ||||
|     if (waitResponse(1000L, res) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     res.replace(GSM_NL "OK" GSM_NL, ""); | ||||
|     res.replace(GSM_NL, " "); | ||||
|     res.trim(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_INFO_ATI() | ||||
|  | ||||
|   bool hasSSL() { | ||||
|     return false;  // TODO: For now | ||||
| @@ -345,14 +250,11 @@ public: | ||||
|    * SIM card functions | ||||
|    */ | ||||
|  | ||||
|   bool simUnlock(const char *pin) { | ||||
|     sendAT(GF("+CPIN=\""), pin, GF("\"")); | ||||
|     return waitResponse() == 1; | ||||
|   } | ||||
| TINY_GSM_MODEM_SIM_UNLOCK_CPIN() | ||||
|  | ||||
|   String getSimCCID() { | ||||
|     sendAT(GF("+ICCID")); | ||||
|     if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) { | ||||
|     sendAT(GF("+QCCID")); | ||||
|     if (waitResponse(GF(GSM_NL "+QCCID:")) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     String res = stream.readStringUntil('\n'); | ||||
| @@ -361,19 +263,10 @@ public: | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   String getIMEI() { | ||||
|     sendAT(GF("+GSN")); | ||||
|     if (waitResponse(GF(GSM_NL)) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     String res = stream.readStringUntil('\n'); | ||||
|     waitResponse(); | ||||
|     res.trim(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_IMEI_GSN() | ||||
|  | ||||
|   SimStatus getSimStatus(unsigned long timeout = 10000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { | ||||
|   SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout_ms; ) { | ||||
|       sendAT(GF("+CPIN?")); | ||||
|       if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { | ||||
|         delay(1000); | ||||
| @@ -391,41 +284,15 @@ public: | ||||
|     return SIM_ERROR; | ||||
|   } | ||||
|  | ||||
|   RegStatus getRegistrationStatus() { | ||||
|     sendAT(GF("+CREG?")); | ||||
|     if (waitResponse(GF(GSM_NL "+CREG:")) != 1) { | ||||
|       return REG_UNKNOWN; | ||||
|     } | ||||
|     streamSkipUntil(','); // Skip format (0) | ||||
|     int status = stream.readStringUntil('\n').toInt(); | ||||
|     waitResponse(); | ||||
|     return (RegStatus)status; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_REGISTRATION_XREG(CREG) | ||||
|  | ||||
|   String getOperator() { | ||||
|     sendAT(GF("+COPS?")); | ||||
|     if (waitResponse(GF(GSM_NL "+COPS:")) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     streamSkipUntil('"'); // Skip mode and format | ||||
|     String res = stream.readStringUntil('"'); | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_OPERATOR_COPS() | ||||
|  | ||||
|   /* | ||||
|    * Generic network functions | ||||
|    */ | ||||
|  | ||||
|   int16_t getSignalQuality() { | ||||
|     sendAT(GF("+CSQ")); | ||||
|     if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) { | ||||
|       return 99; | ||||
|     } | ||||
|     int res = stream.readStringUntil(',').toInt(); | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_CSQ() | ||||
|  | ||||
|   bool isNetworkConnected() { | ||||
|     RegStatus s = getRegistrationStatus(); | ||||
| @@ -441,9 +308,12 @@ public: | ||||
|     waitResponse(); | ||||
|   } | ||||
|  | ||||
| TINY_GSM_MODEM_WAIT_FOR_NETWORK() | ||||
|  | ||||
|   /* | ||||
|    * GPRS functions | ||||
|    */ | ||||
|  | ||||
|   bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { | ||||
|     gprsDisconnect(); | ||||
|  | ||||
| @@ -497,18 +367,7 @@ public: | ||||
|     return waitResponse(60000L, GF("DEACT OK"), GF("ERROR")) == 1; | ||||
|   } | ||||
|  | ||||
|   bool isGprsConnected() { | ||||
|     sendAT(GF("+CGATT?")); | ||||
|     if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     int res = stream.readStringUntil('\n').toInt(); | ||||
|     waitResponse(); | ||||
|     if (res != 1) | ||||
|       return false; | ||||
|  | ||||
|     return localIP() != IPAddress(0,0,0,0); | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() | ||||
|  | ||||
|   /* | ||||
|    * IP Address functions | ||||
| @@ -522,6 +381,10 @@ public: | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   IPAddress localIP() { | ||||
|     return TinyGsmIpFromString(getLocalIP()); | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    * Messaging functions | ||||
|    */ | ||||
| @@ -621,7 +484,7 @@ public: | ||||
|   String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|  | ||||
|   /* | ||||
|    * Battery functions | ||||
|    * Battery & temperature functions | ||||
|    */ | ||||
|  | ||||
|   // Use: float vBatt = modem.getBattVoltage() / 1000.0; | ||||
| @@ -630,10 +493,11 @@ public: | ||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||
|       return 0; | ||||
|     } | ||||
|     streamSkipUntil(','); // Skip | ||||
|     streamSkipUntil(','); // Skip | ||||
|  | ||||
|     streamSkipUntil(','); // Skip battery charge status | ||||
|     streamSkipUntil(','); // Skip battery charge level | ||||
|     // return voltage in mV | ||||
|     uint16_t res = stream.readStringUntil(',').toInt(); | ||||
|     // Wait for final OK | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
| @@ -643,21 +507,66 @@ public: | ||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     stream.readStringUntil(','); | ||||
|     streamSkipUntil(','); // Skip battery charge status | ||||
|     // Read battery charge level | ||||
|     int res = stream.readStringUntil(',').toInt(); | ||||
|     // Wait for final OK | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   uint8_t getBattChargeState() { | ||||
|     sendAT(GF("+CBC?")); | ||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     // Read battery charge status | ||||
|     int res = stream.readStringUntil(',').toInt(); | ||||
|     // Wait for final OK | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) { | ||||
|     sendAT(GF("+CBC?")); | ||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     chargeState = stream.readStringUntil(',').toInt(); | ||||
|     percent = stream.readStringUntil(',').toInt(); | ||||
|     milliVolts = stream.readStringUntil('\n').toInt(); | ||||
|     // Wait for final OK | ||||
|     waitResponse(); | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   float getTemperature() { | ||||
|     sendAT(GF("+QTEMP")); | ||||
|     if (waitResponse(GF(GSM_NL "+QTEMP:")) != 1) { | ||||
|       return (float)-9999; | ||||
|     } | ||||
|     streamSkipUntil(','); // Skip mode | ||||
|     // Read charge of thermistor | ||||
|     // milliVolts = stream.readStringUntil(',').toInt(); | ||||
|     streamSkipUntil(','); // Skip thermistor charge | ||||
|     float temp = stream.readStringUntil('\n').toFloat(); | ||||
|     // Wait for final OK | ||||
|     waitResponse(); | ||||
|     return temp; | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    * Client related functions | ||||
|    */ | ||||
|  | ||||
| protected: | ||||
|  | ||||
|   bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false) { | ||||
|   bool modemConnect(const char* host, uint16_t port, uint8_t mux, | ||||
|                     bool ssl = false, int timeout_s = 75) | ||||
|  { | ||||
|     uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; | ||||
|     sendAT(GF("+QIOPEN="), mux, GF("\"TCP"), GF("\",\""), host, GF("\","), port); | ||||
|     int rsp = waitResponse(75000L, | ||||
|     int rsp = waitResponse(timeout_ms, | ||||
|                            GF("CONNECT OK" GSM_NL), | ||||
|                            GF("CONNECT FAIL" GSM_NL), | ||||
|                            GF("ALREADY CONNECT" GSM_NL)); | ||||
| @@ -690,7 +599,6 @@ protected: | ||||
|       } | ||||
|     } | ||||
|     waitResponse(5000L); | ||||
|     maintain();  // look for a very quick response from the remote | ||||
|  | ||||
|     // streamSkipUntil(','); // Skip mux | ||||
|     // return stream.readStringUntil('\n').toInt(); | ||||
| @@ -706,13 +614,10 @@ protected: | ||||
|     sockets[mux]->sock_available = len; | ||||
|  | ||||
|     for (size_t i=0; i<len; i++) { | ||||
|       while (!stream.available()) { TINY_GSM_YIELD(); } | ||||
|       char c = stream.read(); | ||||
|       sockets[mux]->rx.put(c); | ||||
|       TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT | ||||
|     } | ||||
|     waitResponse(); | ||||
|     DBG("### READ:", len, "from", mux); | ||||
|     maintain();  // Listen for a close or other URC | ||||
|     return len; | ||||
|   } | ||||
|  | ||||
| @@ -723,13 +628,12 @@ protected: | ||||
|       streamSkipUntil(','); // Skip total received | ||||
|       streamSkipUntil(','); // Skip have read | ||||
|       result = stream.readStringUntil('\n').toInt(); | ||||
|       DBG("### DATA AVAILABLE:", result, "on", mux); | ||||
|       if (result) DBG("### DATA AVAILABLE:", result, "on", mux); | ||||
|       waitResponse(); | ||||
|     } | ||||
|     if (!result) { | ||||
|       sockets[mux]->sock_connected = modemGetConnected(mux); | ||||
|     } | ||||
|     maintain();  // Listen for a close or other URC | ||||
|     return result; | ||||
|   } | ||||
|  | ||||
| @@ -748,7 +652,6 @@ protected: | ||||
|     int res = stream.readStringUntil(',').toInt(); // socket state | ||||
|  | ||||
|     waitResponse(); | ||||
|     maintain();  // Listen for a close or other URC | ||||
|  | ||||
|     // 0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing | ||||
|     return 2 == res; | ||||
| @@ -760,16 +663,10 @@ public: | ||||
|    Utilities | ||||
|    */ | ||||
|  | ||||
|   template<typename... Args> | ||||
|   void sendAT(Args... cmd) { | ||||
|     streamWrite("AT", cmd..., GSM_NL); | ||||
|     stream.flush(); | ||||
|     TINY_GSM_YIELD(); | ||||
|     //DBG("### AT:", cmd...); | ||||
|   } | ||||
| TINY_GSM_MODEM_STREAM_UTILITIES() | ||||
|  | ||||
|   // TODO: Optimize this! | ||||
|   uint8_t waitResponse(uint32_t timeout, String& data, | ||||
|   uint8_t waitResponse(uint32_t timeout_ms, String& data, | ||||
|                        GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||
|   { | ||||
| @@ -822,7 +719,7 @@ public: | ||||
|           DBG("### Closed: ", mux); | ||||
|         } | ||||
|       } | ||||
|     } while (millis() - startMillis < timeout); | ||||
|     } while (millis() - startMillis < timeout_ms); | ||||
| finish: | ||||
|     if (!index) { | ||||
|       data.trim(); | ||||
| @@ -835,12 +732,12 @@ finish: | ||||
|     return index; | ||||
|   } | ||||
|  | ||||
|   uint8_t waitResponse(uint32_t timeout, | ||||
|   uint8_t waitResponse(uint32_t timeout_ms, | ||||
|                        GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||
|   { | ||||
|     String data; | ||||
|     return waitResponse(timeout, data, r1, r2, r3, r4, r5); | ||||
|     return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5); | ||||
|   } | ||||
|  | ||||
|   uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|   | ||||
| @@ -16,6 +16,10 @@ | ||||
| //#define TINY_GSM_DEBUG Serial | ||||
| //#define TINY_GSM_USE_HEX | ||||
|  | ||||
| #if !defined(TINY_GSM_RX_BUFFER) | ||||
|   #define TINY_GSM_RX_BUFFER 64 | ||||
| #endif | ||||
|  | ||||
| #define TINY_GSM_MUX_COUNT 6 | ||||
|  | ||||
| #include <TinyGsmCommon.h> | ||||
| @@ -41,7 +45,7 @@ enum RegStatus { | ||||
| }; | ||||
|  | ||||
|  | ||||
| class TinyGsmMC60 : public TinyGsmModem | ||||
| class TinyGsmMC60 | ||||
| { | ||||
|  | ||||
| public: | ||||
| @@ -66,36 +70,20 @@ public: | ||||
|     got_data = false; | ||||
|  | ||||
|     at->sockets[mux] = this; | ||||
|     //  ^^ TODO: attach the socket here at init?  Or later at connect? | ||||
|     // Currently done inconsistently between modems | ||||
|  | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
| public: | ||||
|   virtual int connect(const char *host, uint16_t port) { | ||||
|   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||
|     stop(); | ||||
|     TINY_GSM_YIELD(); | ||||
|     rx.clear(); | ||||
|     sock_connected = at->modemConnect(host, port, mux); | ||||
|     // sock_connected = at->modemConnect(host, port, &mux); | ||||
|     // at->sockets[mux] = this; | ||||
|     // ^^ TODO: attach the socet after attempting connection or above at init? | ||||
|     // Currently done inconsistently between modems | ||||
|     sock_connected = at->modemConnect(host, port, mux, false, timeout_s); | ||||
|     return sock_connected; | ||||
|   } | ||||
|  | ||||
|   virtual int connect(IPAddress ip, uint16_t port) { | ||||
|     String host; host.reserve(16); | ||||
|     host += ip[0]; | ||||
|     host += "."; | ||||
|     host += ip[1]; | ||||
|     host += "."; | ||||
|     host += ip[2]; | ||||
|     host += "."; | ||||
|     host += ip[3]; | ||||
|     return connect(host.c_str(), port); | ||||
|   } | ||||
| TINY_GSM_CLIENT_CONNECT_OVERLOADS() | ||||
|  | ||||
|   virtual void stop() { | ||||
|     TINY_GSM_YIELD(); | ||||
| @@ -116,70 +104,13 @@ public: | ||||
|     at->waitResponse(60000L, GF("CLOSED"), GF("CLOSE OK"), GF("ERROR")); | ||||
|   } | ||||
|  | ||||
|   virtual size_t write(const uint8_t *buf, size_t size) { | ||||
|     TINY_GSM_YIELD(); | ||||
|     at->maintain(); | ||||
|     return at->modemSend(buf, size, mux); | ||||
|   } | ||||
| TINY_GSM_CLIENT_WRITE() | ||||
|  | ||||
|   virtual size_t write(uint8_t c) { | ||||
|     return write(&c, 1); | ||||
|   } | ||||
| TINY_GSM_CLIENT_AVAILABLE_NO_BUFFER_CHECK() | ||||
|  | ||||
|   virtual size_t write(const char *str) { | ||||
|     if (str == NULL) return 0; | ||||
|     return write((const uint8_t *)str, strlen(str)); | ||||
|   } | ||||
| TINY_GSM_CLIENT_READ_NO_BUFFER_CHECK() | ||||
|  | ||||
|   virtual int available() { | ||||
|     TINY_GSM_YIELD(); | ||||
|     if (!rx.size()) { | ||||
|       at->maintain(); | ||||
|     } | ||||
|     return rx.size() + sock_available; | ||||
|   } | ||||
|  | ||||
|   virtual int read(uint8_t *buf, size_t size) { | ||||
|     TINY_GSM_YIELD(); | ||||
|     at->maintain(); | ||||
|     size_t cnt = 0; | ||||
|     while (cnt < size) { | ||||
|       size_t chunk = TinyGsmMin(size-cnt, rx.size()); | ||||
|       if (chunk > 0) { | ||||
|         rx.get(buf, chunk); | ||||
|         buf += chunk; | ||||
|         cnt += chunk; | ||||
|         continue; | ||||
|       } | ||||
|       // TODO: Read directly into user buffer? | ||||
|       at->maintain(); | ||||
|       if (sock_available > 0) { | ||||
|         at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux); | ||||
|       } else { | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|     return cnt; | ||||
|   } | ||||
|  | ||||
|   virtual int read() { | ||||
|     uint8_t c; | ||||
|     if (read(&c, 1) == 1) { | ||||
|       return c; | ||||
|     } | ||||
|     return -1; | ||||
|   } | ||||
|  | ||||
|   virtual int peek() { return -1; } //TODO | ||||
|   virtual void flush() { at->stream.flush(); } | ||||
|  | ||||
|   virtual uint8_t connected() { | ||||
|     if (available()) { | ||||
|       return true; | ||||
|     } | ||||
|     return sock_connected; | ||||
|   } | ||||
|   virtual operator bool() { return connected(); } | ||||
| TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() | ||||
|  | ||||
|   /* | ||||
|    * Extended API | ||||
| @@ -207,11 +138,11 @@ private: | ||||
| //   {} | ||||
| // | ||||
| // public: | ||||
| //   virtual int connect(const char *host, uint16_t port) { | ||||
| //   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||
| //     stop(); | ||||
| //     TINY_GSM_YIELD(); | ||||
| //     rx.clear(); | ||||
| //     sock_connected = at->modemConnect(host, port, mux, true); | ||||
| //     sock_connected = at->modemConnect(host, port, mux, true, timeout_s); | ||||
| //     return sock_connected; | ||||
| //   } | ||||
| // }; | ||||
| @@ -220,7 +151,7 @@ private: | ||||
| public: | ||||
|  | ||||
|   TinyGsmMC60(Stream& stream) | ||||
|     : TinyGsmModem(stream), stream(stream) | ||||
|     : stream(stream) | ||||
|   { | ||||
|     memset(sockets, 0, sizeof(sockets)); | ||||
|   } | ||||
| @@ -229,6 +160,10 @@ public: | ||||
|    * Basic functions | ||||
|    */ | ||||
|  | ||||
|   bool begin(const char* pin = NULL) { | ||||
|     return init(pin); | ||||
|   } | ||||
|  | ||||
|   bool init(const char* pin = NULL) { | ||||
|     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); | ||||
|     if (!testAT()) { | ||||
| @@ -254,31 +189,11 @@ public: | ||||
|       return "Quectel MC60"; | ||||
|   } | ||||
|  | ||||
|   void setBaud(unsigned long baud) { | ||||
|     sendAT(GF("+IPR="), baud); | ||||
|   } | ||||
| TINY_GSM_MODEM_SET_BAUD_IPR() | ||||
|  | ||||
|   bool testAT(unsigned long timeout = 10000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { | ||||
|       sendAT(GF("")); | ||||
|       if (waitResponse(200) == 1) return true; | ||||
|       delay(100); | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
| TINY_GSM_MODEM_TEST_AT() | ||||
|  | ||||
|   void maintain() { | ||||
|     for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) { | ||||
|       GsmClient* sock = sockets[mux]; | ||||
|       if (sock && sock->got_data) { | ||||
|         sock->got_data = false; | ||||
|         sock->sock_available = modemGetAvailable(mux); | ||||
|       } | ||||
|     } | ||||
|     while (stream.available()) { | ||||
|       waitResponse(10, NULL, NULL); | ||||
|     } | ||||
|   } | ||||
| TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS() | ||||
|  | ||||
|   bool factoryDefault() { | ||||
|     sendAT(GF("&FZE0&W"));  // Factory + Reset + Echo Off + Write | ||||
| @@ -289,17 +204,7 @@ public: | ||||
|     return waitResponse() == 1; | ||||
|   } | ||||
|  | ||||
|   String getModemInfo() { | ||||
|     sendAT(GF("I")); | ||||
|     String res; | ||||
|     if (waitResponse(1000L, res) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     res.replace(GSM_NL "OK" GSM_NL, ""); | ||||
|     res.replace(GSM_NL, " "); | ||||
|     res.trim(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_INFO_ATI() | ||||
|  | ||||
|   /* | ||||
|   * under development | ||||
| @@ -367,35 +272,14 @@ public: | ||||
|    * SIM card functions | ||||
|    */ | ||||
|  | ||||
|   bool simUnlock(const char *pin) { | ||||
|     sendAT(GF("+CPIN=\""), pin, GF("\"")); | ||||
|     return waitResponse() == 1; | ||||
|   } | ||||
| TINY_GSM_MODEM_SIM_UNLOCK_CPIN() | ||||
|  | ||||
|   String getSimCCID() { | ||||
|     sendAT(GF("+ICCID")); | ||||
|     if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     String res = stream.readStringUntil('\n'); | ||||
|     waitResponse(); | ||||
|     res.trim(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_SIMCCID_CCID() | ||||
|  | ||||
|   String getIMEI() { | ||||
|     sendAT(GF("+GSN")); | ||||
|     if (waitResponse(GF(GSM_NL)) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     String res = stream.readStringUntil('\n'); | ||||
|     waitResponse(); | ||||
|     res.trim(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_IMEI_GSN() | ||||
|  | ||||
|   SimStatus getSimStatus(unsigned long timeout = 10000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { | ||||
|   SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout_ms; ) { | ||||
|       sendAT(GF("+CPIN?")); | ||||
|       if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { | ||||
|         delay(1000); | ||||
| @@ -415,50 +299,27 @@ public: | ||||
|     return SIM_ERROR; | ||||
|   } | ||||
|  | ||||
|   RegStatus getRegistrationStatus() { | ||||
|     sendAT(GF("+CREG?")); | ||||
|     if (waitResponse(GF(GSM_NL "+CREG:")) != 1) { | ||||
|       return REG_UNKNOWN; | ||||
|     } | ||||
|     streamSkipUntil(','); // Skip format (0) | ||||
|     int status = stream.readStringUntil('\n').toInt(); | ||||
|     waitResponse(); | ||||
|     return (RegStatus)status; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_REGISTRATION_XREG(CREG) | ||||
|  | ||||
|   String getOperator() { | ||||
|     sendAT(GF("+COPS?")); | ||||
|     if (waitResponse(GF(GSM_NL "+COPS:")) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     streamSkipUntil('"'); // Skip mode and format | ||||
|     String res = stream.readStringUntil('"'); | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_OPERATOR_COPS() | ||||
|  | ||||
|   /* | ||||
|    * Generic network functions | ||||
|    */ | ||||
|  | ||||
|   int16_t getSignalQuality() { | ||||
|     sendAT(GF("+CSQ")); | ||||
|     if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) { | ||||
|       return 99; | ||||
|     } | ||||
|     int res = stream.readStringUntil(',').toInt(); | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_CSQ() | ||||
|  | ||||
|   bool isNetworkConnected() { | ||||
|     RegStatus s = getRegistrationStatus(); | ||||
|     return (s == REG_OK_HOME || s == REG_OK_ROAMING); | ||||
|   } | ||||
|  | ||||
| TINY_GSM_MODEM_WAIT_FOR_NETWORK() | ||||
|  | ||||
|   /* | ||||
|    * GPRS functions | ||||
|    */ | ||||
|  | ||||
|   bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { | ||||
|     gprsDisconnect(); | ||||
|  | ||||
| @@ -513,7 +374,7 @@ public: | ||||
|     } | ||||
|  | ||||
|     // Check that we have a local IP address | ||||
|     if (localIP() != 0) { | ||||
|     if (localIP() != IPAddress(0,0,0,0)) { | ||||
|       return true; | ||||
|     } | ||||
|  | ||||
| @@ -525,18 +386,7 @@ public: | ||||
|     return waitResponse(60000L, GF("DEACT OK"), GF("ERROR")) == 1; | ||||
|   } | ||||
|  | ||||
|   bool isGprsConnected() { | ||||
|     sendAT(GF("+CGATT?")); | ||||
|     if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     int res = stream.readStringUntil('\n').toInt(); | ||||
|     waitResponse(); | ||||
|     if (res != 1) | ||||
|       return false; | ||||
|  | ||||
|     return localIP() != IPAddress(0,0,0,0); | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() | ||||
|  | ||||
|   /* | ||||
|    * IP Address functions | ||||
| @@ -550,6 +400,10 @@ public: | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   IPAddress localIP() { | ||||
|     return TinyGsmIpFromString(getLocalIP()); | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    * Messaging functions | ||||
|    */ | ||||
| @@ -649,7 +503,7 @@ public: | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    * Battery functions | ||||
|    * Battery & temperature functions | ||||
|    */ | ||||
|  | ||||
|   // Use: float vBatt = modem.getBattVoltage() / 1000.0; | ||||
| @@ -658,10 +512,11 @@ public: | ||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||
|       return 0; | ||||
|     } | ||||
|     streamSkipUntil(','); // Skip | ||||
|     streamSkipUntil(','); // Skip | ||||
|  | ||||
|     streamSkipUntil(','); // Skip battery charge status | ||||
|     streamSkipUntil(','); // Skip battery charge level | ||||
|     // return voltage in mV | ||||
|     uint16_t res = stream.readStringUntil(',').toInt(); | ||||
|     // Wait for final OK | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
| @@ -671,21 +526,53 @@ public: | ||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     stream.readStringUntil(','); | ||||
|     streamSkipUntil(','); // Skip battery charge status | ||||
|     // Read battery charge level | ||||
|     int res = stream.readStringUntil(',').toInt(); | ||||
|     // Wait for final OK | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   uint8_t getBattChargeState() { | ||||
|     sendAT(GF("+CBC?")); | ||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     // Read battery charge status | ||||
|     int res = stream.readStringUntil(',').toInt(); | ||||
|     // Wait for final OK | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) { | ||||
|     sendAT(GF("+CBC?")); | ||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     chargeState = stream.readStringUntil(',').toInt(); | ||||
|     percent = stream.readStringUntil(',').toInt(); | ||||
|     milliVolts = stream.readStringUntil('\n').toInt(); | ||||
|     // Wait for final OK | ||||
|     waitResponse(); | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   float getTemperature() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|  | ||||
|   /* | ||||
|    * Client related functions | ||||
|    */ | ||||
|  | ||||
| protected: | ||||
|  | ||||
|   bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false) { | ||||
|   bool modemConnect(const char* host, uint16_t port, uint8_t mux, | ||||
|                     bool ssl = false, int timeout_s = 75) | ||||
|  { | ||||
|     uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; | ||||
|     sendAT(GF("+QIOPEN="), mux, GF("\"TCP"), GF("\",\""), host, GF("\","), port); | ||||
|     int rsp = waitResponse(75000L, | ||||
|     int rsp = waitResponse(timeout_ms, | ||||
|                            GF("CONNECT OK" GSM_NL), | ||||
|                            GF("CONNECT FAIL" GSM_NL), | ||||
|                            GF("ALREADY CONNECT" GSM_NL)); | ||||
| @@ -722,7 +609,6 @@ protected: | ||||
|     // streamSkipUntil(','); // Skip mux | ||||
|     // return stream.readStringUntil('\n').toInt(); | ||||
|  | ||||
|     maintain();  // look for a very quick response from the remote | ||||
|     return len;  // TODO | ||||
|   } | ||||
|  | ||||
| @@ -735,13 +621,10 @@ protected: | ||||
|     sockets[mux]->sock_available = len; | ||||
|  | ||||
|     for (size_t i=0; i<len; i++) { | ||||
|       while (!stream.available()) { TINY_GSM_YIELD(); } | ||||
|       char c = stream.read(); | ||||
|       sockets[mux]->rx.put(c); | ||||
|       TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT | ||||
|     } | ||||
|     waitResponse(); | ||||
|     DBG("### READ:", len, "from", mux); | ||||
|     maintain();  // Listen for a close or other URC | ||||
|     return len; | ||||
|   } | ||||
|  | ||||
| @@ -752,13 +635,12 @@ protected: | ||||
|       streamSkipUntil(','); // Skip total received | ||||
|       streamSkipUntil(','); // Skip have read | ||||
|       result = stream.readStringUntil('\n').toInt(); | ||||
|       DBG("### DATA AVAILABLE:", result, "on", mux); | ||||
|       if (result) DBG("### DATA AVAILABLE:", result, "on", mux); | ||||
|       waitResponse(); | ||||
|     } | ||||
|     if (!result) { | ||||
|       sockets[mux]->sock_connected = modemGetConnected(mux); | ||||
|     } | ||||
|     maintain();  // Listen for a close or other URC | ||||
|     return result; | ||||
|   } | ||||
|  | ||||
| @@ -777,7 +659,6 @@ protected: | ||||
|     int res = stream.readStringUntil(',').toInt(); // socket state | ||||
|  | ||||
|     waitResponse(); | ||||
|     maintain();  // Listen for a close or other URC | ||||
|  | ||||
|     // 0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing | ||||
|     return 2 == res; | ||||
| @@ -789,16 +670,10 @@ public: | ||||
|    Utilities | ||||
|    */ | ||||
|  | ||||
|   template<typename... Args> | ||||
|   void sendAT(Args... cmd) { | ||||
|     streamWrite("AT", cmd..., GSM_NL); | ||||
|     stream.flush(); | ||||
|     TINY_GSM_YIELD(); | ||||
|     //DBG("### AT:", cmd...); | ||||
|   } | ||||
| TINY_GSM_MODEM_STREAM_UTILITIES() | ||||
|  | ||||
|   // TODO: Optimize this! | ||||
|   uint8_t waitResponse(uint32_t timeout, String& data, | ||||
|   uint8_t waitResponse(uint32_t timeout_ms, String& data, | ||||
|                        GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL, GsmConstStr r6=NULL) | ||||
|   { | ||||
| @@ -855,7 +730,7 @@ public: | ||||
|           DBG("### Closed: ", mux); | ||||
|         } | ||||
|       } | ||||
|     } while (millis() - startMillis < timeout); | ||||
|     } while (millis() - startMillis < timeout_ms); | ||||
| finish: | ||||
|     if (!index) { | ||||
|       data.trim(); | ||||
| @@ -868,12 +743,12 @@ finish: | ||||
|     return index; | ||||
|   } | ||||
|  | ||||
|   uint8_t waitResponse(uint32_t timeout, | ||||
|   uint8_t waitResponse(uint32_t timeout_ms, | ||||
|                        GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL, GsmConstStr r6=NULL) | ||||
|   { | ||||
|     String data; | ||||
|     return waitResponse(timeout, data, r1, r2, r3, r4, r5, r6); | ||||
|     return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5, r6); | ||||
|   } | ||||
|  | ||||
|   uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
| #ifndef TinyGsmClientSIM7000_h | ||||
| #define TinyGsmClientSIM7000_h | ||||
|  | ||||
| #define TINY_GSM_DEBUG Serial | ||||
| // #define TINY_GSM_DEBUG Serial | ||||
| //#define TINY_GSM_USE_HEX | ||||
|  | ||||
| #if !defined(TINY_GSM_RX_BUFFER) | ||||
| @@ -45,7 +45,7 @@ enum TinyGSMDateTimeFormat { | ||||
|   DATE_DATE = 2 | ||||
| }; | ||||
|  | ||||
| class TinyGsmSim7000 : public TinyGsmModem | ||||
| class TinyGsmSim7000 | ||||
| { | ||||
|  | ||||
| public: | ||||
| @@ -71,36 +71,20 @@ public: | ||||
|     got_data = false; | ||||
|  | ||||
|     at->sockets[mux] = this; | ||||
|     //  ^^ TODO: attach the socket here at init?  Or later at connect? | ||||
|     // Currently done inconsistently between modems | ||||
|  | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
| public: | ||||
|   virtual int connect(const char *host, uint16_t port) { | ||||
|   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||
|     stop(); | ||||
|     TINY_GSM_YIELD(); | ||||
|     rx.clear(); | ||||
|     sock_connected = at->modemConnect(host, port, mux); | ||||
|     // sock_connected = at->modemConnect(host, port, &mux); | ||||
|     // at->sockets[mux] = this; | ||||
|     // ^^ TODO: attach the socet after attempting connection or above at init? | ||||
|     // Currently done inconsistently between modems | ||||
|     sock_connected = at->modemConnect(host, port, mux, false, timeout_s); | ||||
|     return sock_connected; | ||||
|   } | ||||
|  | ||||
|   virtual int connect(IPAddress ip, uint16_t port) { | ||||
|     String host; host.reserve(16); | ||||
|     host += ip[0]; | ||||
|     host += "."; | ||||
|     host += ip[1]; | ||||
|     host += "."; | ||||
|     host += ip[2]; | ||||
|     host += "."; | ||||
|     host += ip[3]; | ||||
|     return connect(host.c_str(), port); | ||||
|   } | ||||
| TINY_GSM_CLIENT_CONNECT_OVERLOADS() | ||||
|  | ||||
|   virtual void stop() { | ||||
|     TINY_GSM_YIELD(); | ||||
| @@ -121,86 +105,13 @@ public: | ||||
|     at->waitResponse(); | ||||
|   } | ||||
|  | ||||
|   virtual size_t write(const uint8_t *buf, size_t size) { | ||||
|     TINY_GSM_YIELD(); | ||||
|     at->maintain(); | ||||
|     return at->modemSend(buf, size, mux); | ||||
|   } | ||||
| TINY_GSM_CLIENT_WRITE() | ||||
|  | ||||
|   virtual size_t write(uint8_t c) { | ||||
|     return write(&c, 1); | ||||
|   } | ||||
| TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK() | ||||
|  | ||||
|   virtual size_t write(const char *str) { | ||||
|     if (str == NULL) return 0; | ||||
|     return write((const uint8_t *)str, strlen(str)); | ||||
|   } | ||||
| TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK() | ||||
|  | ||||
|   virtual int available() { | ||||
|     TINY_GSM_YIELD(); | ||||
|     if (!rx.size()) { | ||||
|       // TODO:  Is this needed for SIM7000? | ||||
|       // Workaround: sometimes SIM7000 forgets to notify about data arrival. | ||||
|       // TODO: Currently we ping the module periodically, | ||||
|       // but maybe there's a better indicator that we need to poll | ||||
|       if (millis() - prev_check > 250) { | ||||
|         got_data = true; | ||||
|         prev_check = millis(); | ||||
|       } | ||||
|       at->maintain(); | ||||
|     } | ||||
|     return rx.size() + sock_available; | ||||
|   } | ||||
|  | ||||
|   virtual int read(uint8_t *buf, size_t size) { | ||||
|     TINY_GSM_YIELD(); | ||||
|     at->maintain(); | ||||
|     size_t cnt = 0; | ||||
|     while (cnt < size) { | ||||
|       size_t chunk = TinyGsmMin(size-cnt, rx.size()); | ||||
|       if (chunk > 0) { | ||||
|         rx.get(buf, chunk); | ||||
|         buf += chunk; | ||||
|         cnt += chunk; | ||||
|         continue; | ||||
|       } | ||||
|       // TODO:  Is this needed for SIM7000? | ||||
|       // Workaround: sometimes SIM7000 forgets to notify about data arrival. | ||||
|       // TODO: Currently we ping the module periodically, | ||||
|       // but maybe there's a better indicator that we need to poll | ||||
|       if (millis() - prev_check > 250) { | ||||
|         got_data = true; | ||||
|         prev_check = millis(); | ||||
|       } | ||||
|       // TODO: Read directly into user buffer? | ||||
|       at->maintain(); | ||||
|       if (sock_available > 0) { | ||||
|         at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux); | ||||
|       } else { | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|     return cnt; | ||||
|   } | ||||
|  | ||||
|   virtual int read() { | ||||
|     uint8_t c; | ||||
|     if (read(&c, 1) == 1) { | ||||
|       return c; | ||||
|     } | ||||
|     return -1; | ||||
|   } | ||||
|  | ||||
|   virtual int peek() { return -1; } //TODO | ||||
|   virtual void flush() { at->stream.flush(); } | ||||
|  | ||||
|   virtual uint8_t connected() { | ||||
|     if (available()) { | ||||
|       return true; | ||||
|     } | ||||
|     return sock_connected; | ||||
|   } | ||||
|   virtual operator bool() { return connected(); } | ||||
| TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() | ||||
|  | ||||
|   /* | ||||
|    * Extended API | ||||
| @@ -229,14 +140,11 @@ public: | ||||
|   {} | ||||
|  | ||||
| public: | ||||
|   virtual int connect(const char *host, uint16_t port) { | ||||
|   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||
|     stop(); | ||||
|     TINY_GSM_YIELD(); | ||||
|     rx.clear(); | ||||
|     sock_connected = at->modemConnect(host, port, mux, true); | ||||
|     // sock_connected = at->modemConnect(host, port, &mux, true); | ||||
|     // at->sockets[mux] = this; | ||||
|     // TODO:  When is the socket attached? | ||||
|     sock_connected = at->modemConnect(host, port, mux, true, timeout_s); | ||||
|     return sock_connected; | ||||
|   } | ||||
| }; | ||||
| @@ -245,7 +153,7 @@ public: | ||||
| public: | ||||
|  | ||||
|   TinyGsmSim7000(Stream& stream) | ||||
|     : TinyGsmModem(stream), stream(stream) | ||||
|     : stream(stream) | ||||
|   { | ||||
|     memset(sockets, 0, sizeof(sockets)); | ||||
|   } | ||||
| @@ -254,6 +162,10 @@ public: | ||||
|    * Basic functions | ||||
|    */ | ||||
|  | ||||
|   bool begin(const char* pin = NULL) { | ||||
|     return init(pin); | ||||
|   } | ||||
|  | ||||
|   bool init(const char* pin = NULL) { | ||||
|     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); | ||||
|     if (!testAT()) { | ||||
| @@ -272,48 +184,17 @@ public: | ||||
|     return "SIMCom SIM7000"; | ||||
|   } | ||||
|  | ||||
|   void setBaud(unsigned long baud) { | ||||
|     sendAT(GF("+IPR="), baud); | ||||
|   } | ||||
| TINY_GSM_MODEM_SET_BAUD_IPR() | ||||
|  | ||||
|   bool testAT(unsigned long timeout = 10000L) { | ||||
|     //streamWrite(GF("AAAAA" GSM_NL));  // TODO: extra A's to help detect the baud rate | ||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { | ||||
|       sendAT(GF("")); | ||||
|       if (waitResponse(200) == 1) return true; | ||||
|       delay(100); | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
| TINY_GSM_MODEM_TEST_AT() | ||||
|  | ||||
|   void maintain() { | ||||
|     for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) { | ||||
|       GsmClient* sock = sockets[mux]; | ||||
|       if (sock && sock->got_data) { | ||||
|         sock->got_data = false; | ||||
|         sock->sock_available = modemGetAvailable(mux); | ||||
|       } | ||||
|     } | ||||
|     while (stream.available()) { | ||||
|       waitResponse(10, NULL, NULL); | ||||
|     } | ||||
|   } | ||||
| TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS() | ||||
|  | ||||
|   bool factoryDefault() {  // these commands aren't supported | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   String getModemInfo() { | ||||
|     sendAT(GF("I")); | ||||
|     String res; | ||||
|     if (waitResponse(1000L, res) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     res.replace(GSM_NL "OK" GSM_NL, ""); | ||||
|     res.replace(GSM_NL, " "); | ||||
|     res.trim(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_INFO_ATI() | ||||
|  | ||||
|   bool hasSSL() { | ||||
|     return false;  // TODO:  Module supports SSL, but not yet implemented | ||||
| @@ -382,35 +263,14 @@ public: | ||||
|    * SIM card functions | ||||
|    */ | ||||
|  | ||||
|   bool simUnlock(const char *pin) { | ||||
|     sendAT(GF("+CPIN=\""), pin, GF("\"")); | ||||
|     return waitResponse() == 1; | ||||
|   } | ||||
| TINY_GSM_MODEM_SIM_UNLOCK_CPIN() | ||||
|  | ||||
|   String getSimCCID() { | ||||
|     sendAT(GF("+ICCID")); | ||||
|     if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     String res = stream.readStringUntil('\n'); | ||||
|     waitResponse(); | ||||
|     res.trim(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_SIMCCID_CCID() | ||||
|  | ||||
|   String getIMEI() { | ||||
|     sendAT(GF("+GSN")); | ||||
|     if (waitResponse(GF(GSM_NL)) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     String res = stream.readStringUntil('\n'); | ||||
|     waitResponse(); | ||||
|     res.trim(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_IMEI_GSN() | ||||
|  | ||||
|   SimStatus getSimStatus(unsigned long timeout = 10000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { | ||||
|   SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout_ms; ) { | ||||
|       sendAT(GF("+CPIN?")); | ||||
|       if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { | ||||
|         delay(1000); | ||||
| @@ -428,42 +288,15 @@ public: | ||||
|     return SIM_ERROR; | ||||
|   } | ||||
|  | ||||
|   RegStatus getRegistrationStatus() { | ||||
|     sendAT(GF("+CGREG?")); | ||||
|     // TODO:  Shouldn't this be CEREG for the EPS status of this 4G module? | ||||
|     if (waitResponse(GF(GSM_NL "+CGREG:")) != 1) { | ||||
|       return REG_UNKNOWN; | ||||
|     } | ||||
|     streamSkipUntil(','); // Skip format (0) | ||||
|     int status = stream.readStringUntil('\n').toInt(); | ||||
|     waitResponse(); | ||||
|     return (RegStatus)status; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_REGISTRATION_XREG(CGREG) | ||||
|  | ||||
|   String getOperator() { | ||||
|     sendAT(GF("+COPS?")); | ||||
|     if (waitResponse(GF(GSM_NL "+COPS:")) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     streamSkipUntil('"'); // Skip mode and format | ||||
|     String res = stream.readStringUntil('"'); | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_OPERATOR_COPS() | ||||
|  | ||||
|   /* | ||||
|    * Generic network functions | ||||
|    */ | ||||
|  | ||||
|   int16_t getSignalQuality() { | ||||
|     sendAT(GF("+CSQ")); | ||||
|     if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) { | ||||
|       return 99; | ||||
|     } | ||||
|     int res = stream.readStringUntil(',').toInt(); | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_CSQ() | ||||
|  | ||||
|   bool isNetworkConnected() { | ||||
|     RegStatus s = getRegistrationStatus(); | ||||
| @@ -480,6 +313,8 @@ public: | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
| TINY_GSM_MODEM_WAIT_FOR_NETWORK() | ||||
|  | ||||
|   String setNetworkMode(uint8_t mode) { | ||||
|       sendAT(GF("+CNMP="), mode); | ||||
|       if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { | ||||
| @@ -514,6 +349,7 @@ public: | ||||
|   /* | ||||
|    * GPRS functions | ||||
|    */ | ||||
|  | ||||
|   bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { | ||||
|     gprsDisconnect(); | ||||
|  | ||||
| @@ -641,6 +477,10 @@ public: | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   IPAddress localIP() { | ||||
|     return TinyGsmIpFromString(getLocalIP()); | ||||
|   } | ||||
|  | ||||
|  | ||||
|   /* | ||||
|    * Phone Call functions | ||||
| @@ -814,6 +654,7 @@ public: | ||||
|   /* | ||||
|    * Time functions | ||||
|    */ | ||||
|  | ||||
|   String getGSMDateTime(TinyGSMDateTimeFormat format) { | ||||
|     sendAT(GF("+CCLK?")); | ||||
|     if (waitResponse(2000L, GF(GSM_NL "+CCLK: \"")) != 1) { | ||||
| @@ -887,15 +728,16 @@ public: | ||||
|   /* | ||||
|    * Battery functions | ||||
|    */ | ||||
|  | ||||
|   // Use: float vBatt = modem.getBattVoltage() / 1000.0; | ||||
|   uint16_t getBattVoltage() { | ||||
|     sendAT(GF("+CBC")); | ||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||
|       return 0; | ||||
|     } | ||||
|     streamSkipUntil(','); // Skip | ||||
|     streamSkipUntil(','); // Skip | ||||
|  | ||||
|     streamSkipUntil(','); // Skip battery charge status | ||||
|     streamSkipUntil(','); // Skip battery charge level | ||||
|     // return voltage in mV | ||||
|     uint16_t res = stream.readStringUntil(',').toInt(); | ||||
|     waitResponse(); | ||||
|     return res; | ||||
| @@ -906,22 +748,53 @@ public: | ||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     stream.readStringUntil(','); | ||||
|     streamSkipUntil(','); // Skip battery charge status | ||||
|     // Read battery charge level | ||||
|     int res = stream.readStringUntil(',').toInt(); | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   uint8_t getBattChargeState() { | ||||
|     sendAT(GF("+CBC?")); | ||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     // Read battery charge status | ||||
|     int res = stream.readStringUntil(',').toInt(); | ||||
|     // Wait for final OK | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) { | ||||
|     sendAT(GF("+CBC?")); | ||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     chargeState = stream.readStringUntil(',').toInt(); | ||||
|     percent = stream.readStringUntil(',').toInt(); | ||||
|     milliVolts = stream.readStringUntil('\n').toInt(); | ||||
|     // Wait for final OK | ||||
|     waitResponse(); | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   float getTemperature() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|  | ||||
|   /* | ||||
|    * Client related functions | ||||
|    */ | ||||
|  | ||||
| protected: | ||||
|  | ||||
|   bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false) { | ||||
|   bool modemConnect(const char* host, uint16_t port, uint8_t mux, | ||||
|                     bool ssl = false, int timeout_s = 75) | ||||
|  { | ||||
|     int rsp; | ||||
|     uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; | ||||
|     sendAT(GF("+CIPSTART="), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), port); | ||||
|     rsp = waitResponse(75000L, | ||||
|     rsp = waitResponse(timeout_ms, | ||||
|                        GF("CONNECT OK" GSM_NL), | ||||
|                        GF("CONNECT FAIL" GSM_NL), | ||||
|                        GF("ALREADY CONNECT" GSM_NL), | ||||
| @@ -942,7 +815,6 @@ protected: | ||||
|       return 0; | ||||
|     } | ||||
|     streamSkipUntil(','); // Skip mux | ||||
|     maintain();  // look for a very quick response from the remote | ||||
|     return stream.readStringUntil('\n').toInt(); | ||||
|   } | ||||
|  | ||||
| @@ -963,30 +835,30 @@ protected: | ||||
|     size_t len_requested = stream.readStringUntil(',').toInt(); | ||||
|     //  ^^ Requested number of data bytes (1-1460 bytes)to be read | ||||
|     size_t len_confirmed = stream.readStringUntil('\n').toInt(); | ||||
|     if (len_confirmed > len_requested) { | ||||
|     if (len_confirmed < len_requested) { | ||||
|       DBG(len_requested - len_confirmed, "fewer bytes confirmed than requested!"); | ||||
|     } | ||||
|     sockets[mux]->sock_available = len_confirmed; | ||||
|     // ^^ Confirmed number of data bytes to be read, which may be less than requested. | ||||
|     // 0 indicates that no data can be read. | ||||
|  | ||||
|     for (size_t i=0; i<len_confirmed; i++) { | ||||
|     for (size_t i=0; i<TinyGsmMin(len_confirmed, len_requested) ; i++) { | ||||
|       uint32_t startMillis = millis(); | ||||
| #ifdef TINY_GSM_USE_HEX | ||||
|       while (stream.available() < 2) { TINY_GSM_YIELD(); } | ||||
|       while (stream.available() < 2 && (millis() - startMillis < sockets[mux]->_timeout)) { TINY_GSM_YIELD(); } | ||||
|       char buf[4] = { 0, }; | ||||
|       buf[0] = stream.read(); | ||||
|       buf[1] = stream.read(); | ||||
|       char c = strtol(buf, NULL, 16); | ||||
| #else | ||||
|       while (!stream.available()) { TINY_GSM_YIELD(); } | ||||
|       while (!stream.available() && (millis() - startMillis < sockets[mux]->_timeout)) { TINY_GSM_YIELD(); } | ||||
|       char c = stream.read(); | ||||
| #endif | ||||
|       sockets[mux]->rx.put(c); | ||||
|     } | ||||
|     waitResponse(); | ||||
|     DBG("### READ:", len_confirmed, "from", mux); | ||||
|     maintain();  // Listen for a close or other URC | ||||
|     return len_confirmed; | ||||
|     DBG("### READ:", TinyGsmMin(len_confirmed, len_requested), "from", mux); | ||||
|     return TinyGsmMin(len_confirmed, len_requested); | ||||
|   } | ||||
|  | ||||
|   size_t modemGetAvailable(uint8_t mux) { | ||||
| @@ -1001,7 +873,6 @@ protected: | ||||
|     if (!result) { | ||||
|       sockets[mux]->sock_connected = modemGetConnected(mux); | ||||
|     } | ||||
|     maintain();  // Listen for a close or other URC | ||||
|     return result; | ||||
|   } | ||||
|  | ||||
| @@ -1009,7 +880,6 @@ protected: | ||||
|     sendAT(GF("+CIPSTATUS="), mux); | ||||
|     int res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""), GF(",\"CLOSING\""), GF(",\"INITIAL\"")); | ||||
|     waitResponse(); | ||||
|     maintain();  // Listen for a close or other URC | ||||
|     return 1 == res; | ||||
|   } | ||||
|  | ||||
| @@ -1019,16 +889,10 @@ public: | ||||
|    Utilities | ||||
|    */ | ||||
|  | ||||
|   template<typename... Args> | ||||
|   void sendAT(Args... cmd) { | ||||
|     streamWrite("AT", cmd..., GSM_NL); | ||||
|     stream.flush(); | ||||
|     TINY_GSM_YIELD(); | ||||
|     //DBG("### AT:", cmd...); | ||||
|   } | ||||
| TINY_GSM_MODEM_STREAM_UTILITIES() | ||||
|  | ||||
|   // TODO: Optimize this! | ||||
|   uint8_t waitResponse(uint32_t timeout, String& data, | ||||
|   uint8_t waitResponse(uint32_t timeout_ms, String& data, | ||||
|                        GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||
|   { | ||||
| @@ -1094,7 +958,7 @@ public: | ||||
|           DBG("### Closed: ", mux); | ||||
|         } | ||||
|       } | ||||
|     } while (millis() - startMillis < timeout); | ||||
|     } while (millis() - startMillis < timeout_ms); | ||||
| finish: | ||||
|     if (!index) { | ||||
|       data.trim(); | ||||
| @@ -1107,12 +971,12 @@ finish: | ||||
|     return index; | ||||
|   } | ||||
|  | ||||
|   uint8_t waitResponse(uint32_t timeout, | ||||
|   uint8_t waitResponse(uint32_t timeout_ms, | ||||
|                        GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||
|   { | ||||
|     String data; | ||||
|     return waitResponse(timeout, data, r1, r2, r3, r4, r5); | ||||
|     return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5); | ||||
|   } | ||||
|  | ||||
|   uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|   | ||||
| @@ -13,6 +13,10 @@ | ||||
| //#define TINY_GSM_DEBUG Serial | ||||
| //#define TINY_GSM_USE_HEX | ||||
|  | ||||
| #if !defined(TINY_GSM_RX_BUFFER) | ||||
|   #define TINY_GSM_RX_BUFFER 64 | ||||
| #endif | ||||
|  | ||||
| #define TINY_GSM_MUX_COUNT 5 | ||||
|  | ||||
| #include <TinyGsmCommon.h> | ||||
| @@ -42,7 +46,7 @@ enum TinyGSMDateTimeFormat { | ||||
|   DATE_DATE = 2 | ||||
| }; | ||||
|  | ||||
| class TinyGsmSim800 : public TinyGsmModem | ||||
| class TinyGsmSim800 | ||||
| { | ||||
|  | ||||
| public: | ||||
| @@ -68,36 +72,20 @@ public: | ||||
|     got_data = false; | ||||
|  | ||||
|     at->sockets[mux] = this; | ||||
|     //  ^^ TODO: attach the socket here at init?  Or later at connect? | ||||
|     // Currently done inconsistently between modems | ||||
|  | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
| public: | ||||
|   virtual int connect(const char *host, uint16_t port) { | ||||
|   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||
|     stop(); | ||||
|     TINY_GSM_YIELD(); | ||||
|     rx.clear(); | ||||
|     sock_connected = at->modemConnect(host, port, mux); | ||||
|     // sock_connected = at->modemConnect(host, port, &mux); | ||||
|     // at->sockets[mux] = this; | ||||
|     // ^^ TODO: attach the socet after attempting connection or above at init? | ||||
|     // Currently done inconsistently between modems | ||||
|     sock_connected = at->modemConnect(host, port, mux, false, timeout_s); | ||||
|     return sock_connected; | ||||
|   } | ||||
|  | ||||
|   virtual int connect(IPAddress ip, uint16_t port) { | ||||
|     String host; host.reserve(16); | ||||
|     host += ip[0]; | ||||
|     host += "."; | ||||
|     host += ip[1]; | ||||
|     host += "."; | ||||
|     host += ip[2]; | ||||
|     host += "."; | ||||
|     host += ip[3]; | ||||
|     return connect(host.c_str(), port); | ||||
|   } | ||||
| TINY_GSM_CLIENT_CONNECT_OVERLOADS() | ||||
|  | ||||
|   virtual void stop() { | ||||
|     TINY_GSM_YIELD(); | ||||
| @@ -118,84 +106,13 @@ public: | ||||
|     at->waitResponse(); | ||||
|   } | ||||
|  | ||||
|   virtual size_t write(const uint8_t *buf, size_t size) { | ||||
|     TINY_GSM_YIELD(); | ||||
|     at->maintain(); | ||||
|     return at->modemSend(buf, size, mux); | ||||
|   } | ||||
| TINY_GSM_CLIENT_WRITE() | ||||
|  | ||||
|   virtual size_t write(uint8_t c) { | ||||
|     return write(&c, 1); | ||||
|   } | ||||
| TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK() | ||||
|  | ||||
|   virtual size_t write(const char *str) { | ||||
|     if (str == NULL) return 0; | ||||
|     return write((const uint8_t *)str, strlen(str)); | ||||
|   } | ||||
| TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK() | ||||
|  | ||||
|   virtual int available() { | ||||
|     TINY_GSM_YIELD(); | ||||
|     if (!rx.size()) { | ||||
|       // Workaround: sometimes SIM800 forgets to notify about data arrival. | ||||
|       // TODO: Currently we ping the module periodically, | ||||
|       // but maybe there's a better indicator that we need to poll | ||||
|       if (millis() - prev_check > 250) { | ||||
|         got_data = true; | ||||
|         prev_check = millis(); | ||||
|       } | ||||
|       at->maintain(); | ||||
|     } | ||||
|     return rx.size() + sock_available; | ||||
|   } | ||||
|  | ||||
|   virtual int read(uint8_t *buf, size_t size) { | ||||
|     TINY_GSM_YIELD(); | ||||
|     at->maintain(); | ||||
|     size_t cnt = 0; | ||||
|     while (cnt < size) { | ||||
|       size_t chunk = TinyGsmMin(size-cnt, rx.size()); | ||||
|       if (chunk > 0) { | ||||
|         rx.get(buf, chunk); | ||||
|         buf += chunk; | ||||
|         cnt += chunk; | ||||
|         continue; | ||||
|       } | ||||
|       // Workaround: sometimes SIM800 forgets to notify about data arrival. | ||||
|       // TODO: Currently we ping the module periodically, | ||||
|       // but maybe there's a better indicator that we need to poll | ||||
|       if (millis() - prev_check > 250) { | ||||
|         got_data = true; | ||||
|         prev_check = millis(); | ||||
|       } | ||||
|       // TODO: Read directly into user buffer? | ||||
|       at->maintain(); | ||||
|       if (sock_available > 0) { | ||||
|         at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux); | ||||
|       } else { | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|     return cnt; | ||||
|   } | ||||
|  | ||||
|   virtual int read() { | ||||
|     uint8_t c; | ||||
|     if (read(&c, 1) == 1) { | ||||
|       return c; | ||||
|     } | ||||
|     return -1; | ||||
|   } | ||||
|  | ||||
|   virtual int peek() { return -1; } //TODO | ||||
|   virtual void flush() { at->stream.flush(); } | ||||
|  | ||||
|   virtual uint8_t connected() { | ||||
|     if (available()) { | ||||
|       return true; | ||||
|     } | ||||
|     return sock_connected; | ||||
|   } | ||||
|   virtual operator bool() { return connected(); } | ||||
| TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() | ||||
|  | ||||
|   /* | ||||
|    * Extended API | ||||
| @@ -224,14 +141,11 @@ public: | ||||
|   {} | ||||
|  | ||||
| public: | ||||
|   virtual int connect(const char *host, uint16_t port) { | ||||
|   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||
|     stop(); | ||||
|     TINY_GSM_YIELD(); | ||||
|     rx.clear(); | ||||
|     sock_connected = at->modemConnect(host, port, mux, true); | ||||
|     // sock_connected = at->modemConnect(host, port, &mux, true); | ||||
|     // at->sockets[mux] = this; | ||||
|     // TODO:  When is the socket attached? | ||||
|     sock_connected = at->modemConnect(host, port, mux, true, timeout_s); | ||||
|     return sock_connected; | ||||
|   } | ||||
| }; | ||||
| @@ -240,7 +154,7 @@ public: | ||||
| public: | ||||
|  | ||||
|   TinyGsmSim800(Stream& stream) | ||||
|     : TinyGsmModem(stream), stream(stream) | ||||
|     : stream(stream) | ||||
|   { | ||||
|     memset(sockets, 0, sizeof(sockets)); | ||||
|   } | ||||
| @@ -249,6 +163,10 @@ public: | ||||
|    * Basic functions | ||||
|    */ | ||||
|  | ||||
|   bool begin(const char* pin = NULL) { | ||||
|     return init(pin); | ||||
|   } | ||||
|  | ||||
|   bool init(const char* pin = NULL) { | ||||
|     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); | ||||
|     if (!testAT()) { | ||||
| @@ -278,32 +196,11 @@ public: | ||||
|     return "SIMCom SIM800"; | ||||
|   } | ||||
|  | ||||
|   void setBaud(unsigned long baud) { | ||||
|     sendAT(GF("+IPR="), baud); | ||||
|   } | ||||
| TINY_GSM_MODEM_SET_BAUD_IPR() | ||||
|  | ||||
|   bool testAT(unsigned long timeout = 10000L) { | ||||
|     //streamWrite(GF("AAAAA" GSM_NL));  // TODO: extra A's to help detect the baud rate | ||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { | ||||
|       sendAT(GF("")); | ||||
|       if (waitResponse(200) == 1) return true; | ||||
|       delay(100); | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
| TINY_GSM_MODEM_TEST_AT() | ||||
|  | ||||
|   void maintain() { | ||||
|     for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) { | ||||
|       GsmClient* sock = sockets[mux]; | ||||
|       if (sock && sock->got_data) { | ||||
|         sock->got_data = false; | ||||
|         sock->sock_available = modemGetAvailable(mux); | ||||
|       } | ||||
|     } | ||||
|     while (stream.available()) { | ||||
|       waitResponse(10, NULL, NULL); | ||||
|     } | ||||
|   } | ||||
| TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS() | ||||
|  | ||||
|   bool factoryDefault() { | ||||
|     sendAT(GF("&FZE0&W"));  // Factory + Reset + Echo Off + Write | ||||
| @@ -320,17 +217,7 @@ public: | ||||
|     return waitResponse() == 1; | ||||
|   } | ||||
|  | ||||
|   String getModemInfo() { | ||||
|     sendAT(GF("I")); | ||||
|     String res; | ||||
|     if (waitResponse(1000L, res) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     res.replace(GSM_NL "OK" GSM_NL, ""); | ||||
|     res.replace(GSM_NL, " "); | ||||
|     res.trim(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_INFO_ATI() | ||||
|  | ||||
|   bool hasSSL() { | ||||
| #if defined(TINY_GSM_MODEM_SIM900) | ||||
| @@ -408,35 +295,14 @@ public: | ||||
|    * SIM card functions | ||||
|    */ | ||||
|  | ||||
|   bool simUnlock(const char *pin) { | ||||
|     sendAT(GF("+CPIN=\""), pin, GF("\"")); | ||||
|     return waitResponse() == 1; | ||||
|   } | ||||
| TINY_GSM_MODEM_SIM_UNLOCK_CPIN() | ||||
|  | ||||
|   String getSimCCID() { | ||||
|     sendAT(GF("+ICCID")); | ||||
|     if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     String res = stream.readStringUntil('\n'); | ||||
|     waitResponse(); | ||||
|     res.trim(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_SIMCCID_CCID() | ||||
|  | ||||
|   String getIMEI() { | ||||
|     sendAT(GF("+GSN")); | ||||
|     if (waitResponse(GF(GSM_NL)) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     String res = stream.readStringUntil('\n'); | ||||
|     waitResponse(); | ||||
|     res.trim(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_IMEI_GSN() | ||||
|  | ||||
|   SimStatus getSimStatus(unsigned long timeout = 10000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { | ||||
|   SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout_ms; ) { | ||||
|       sendAT(GF("+CPIN?")); | ||||
|       if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { | ||||
|         delay(1000); | ||||
| @@ -454,50 +320,27 @@ public: | ||||
|     return SIM_ERROR; | ||||
|   } | ||||
|  | ||||
|   RegStatus getRegistrationStatus() { | ||||
|     sendAT(GF("+CREG?")); | ||||
|     if (waitResponse(GF(GSM_NL "+CREG:")) != 1) { | ||||
|       return REG_UNKNOWN; | ||||
|     } | ||||
|     streamSkipUntil(','); // Skip format (0) | ||||
|     int status = stream.readStringUntil('\n').toInt(); | ||||
|     waitResponse(); | ||||
|     return (RegStatus)status; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_REGISTRATION_XREG(CREG) | ||||
|  | ||||
|   String getOperator() { | ||||
|     sendAT(GF("+COPS?")); | ||||
|     if (waitResponse(GF(GSM_NL "+COPS:")) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     streamSkipUntil('"'); // Skip mode and format | ||||
|     String res = stream.readStringUntil('"'); | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_OPERATOR_COPS() | ||||
|  | ||||
|   /* | ||||
|    * Generic network functions | ||||
|    */ | ||||
|  | ||||
|   int16_t getSignalQuality() { | ||||
|     sendAT(GF("+CSQ")); | ||||
|     if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) { | ||||
|       return 99; | ||||
|     } | ||||
|     int res = stream.readStringUntil(',').toInt(); | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_CSQ() | ||||
|  | ||||
|   bool isNetworkConnected() { | ||||
|     RegStatus s = getRegistrationStatus(); | ||||
|     return (s == REG_OK_HOME || s == REG_OK_ROAMING); | ||||
|   } | ||||
|  | ||||
| TINY_GSM_MODEM_WAIT_FOR_NETWORK() | ||||
|  | ||||
|   /* | ||||
|    * GPRS functions | ||||
|    */ | ||||
|  | ||||
|   bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { | ||||
|     gprsDisconnect(); | ||||
|  | ||||
| @@ -616,6 +459,7 @@ public: | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|  | ||||
|   /* | ||||
|    * IP Address functions | ||||
|    */ | ||||
| @@ -632,6 +476,9 @@ public: | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   IPAddress localIP() { | ||||
|     return TinyGsmIpFromString(getLocalIP()); | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    * Phone Call functions | ||||
| @@ -775,6 +622,7 @@ public: | ||||
|   /* | ||||
|    * Time functions | ||||
|    */ | ||||
|  | ||||
|   String getGSMDateTime(TinyGSMDateTimeFormat format) { | ||||
|     sendAT(GF("+CCLK?")); | ||||
|     if (waitResponse(2000L, GF(GSM_NL "+CCLK: \"")) != 1) { | ||||
| @@ -799,7 +647,7 @@ public: | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    * Battery functions | ||||
|    * Battery & temperature functions | ||||
|    */ | ||||
|  | ||||
|   // Use: float vBatt = modem.getBattVoltage() / 1000.0; | ||||
| @@ -808,10 +656,11 @@ public: | ||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||
|       return 0; | ||||
|     } | ||||
|     streamSkipUntil(','); // Skip | ||||
|     streamSkipUntil(','); // Skip | ||||
|  | ||||
|     streamSkipUntil(','); // Skip battery charge status | ||||
|     streamSkipUntil(','); // Skip battery charge level | ||||
|     // return voltage in mV | ||||
|     uint16_t res = stream.readStringUntil(',').toInt(); | ||||
|     // Wait for final OK | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
| @@ -821,20 +670,52 @@ public: | ||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     stream.readStringUntil(','); | ||||
|     streamSkipUntil(','); // Skip battery charge status | ||||
|     // Read battery charge level | ||||
|     int res = stream.readStringUntil(',').toInt(); | ||||
|     // Wait for final OK | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   uint8_t getBattChargeState() { | ||||
|     sendAT(GF("+CBC?")); | ||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     // Read battery charge status | ||||
|     int res = stream.readStringUntil(',').toInt(); | ||||
|     // Wait for final OK | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) { | ||||
|     sendAT(GF("+CBC?")); | ||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     chargeState = stream.readStringUntil(',').toInt(); | ||||
|     percent = stream.readStringUntil(',').toInt(); | ||||
|     milliVolts = stream.readStringUntil('\n').toInt(); | ||||
|     // Wait for final OK | ||||
|     waitResponse(); | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   float getTemperature() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|  | ||||
|   /* | ||||
|    * Client related functions | ||||
|    */ | ||||
|  | ||||
| protected: | ||||
|  | ||||
|   bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false) { | ||||
|   bool modemConnect(const char* host, uint16_t port, uint8_t mux, | ||||
|                     bool ssl = false, int timeout_s = 75) | ||||
|  { | ||||
|     int rsp; | ||||
|     uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; | ||||
| #if !defined(TINY_GSM_MODEM_SIM900) | ||||
|     sendAT(GF("+CIPSSL="), ssl); | ||||
|     rsp = waitResponse(); | ||||
| @@ -843,7 +724,7 @@ protected: | ||||
|     } | ||||
| #endif | ||||
|     sendAT(GF("+CIPSTART="), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), port); | ||||
|     rsp = waitResponse(75000L, | ||||
|     rsp = waitResponse(timeout_ms, | ||||
|                        GF("CONNECT OK" GSM_NL), | ||||
|                        GF("CONNECT FAIL" GSM_NL), | ||||
|                        GF("ALREADY CONNECT" GSM_NL), | ||||
| @@ -864,7 +745,6 @@ protected: | ||||
|       return 0; | ||||
|     } | ||||
|     streamSkipUntil(','); // Skip mux | ||||
|     maintain();  // look for a very quick response from the remote | ||||
|     return stream.readStringUntil('\n').toInt(); | ||||
|   } | ||||
|  | ||||
| @@ -885,30 +765,30 @@ protected: | ||||
|     size_t len_requested = stream.readStringUntil(',').toInt(); | ||||
|     //  ^^ Requested number of data bytes (1-1460 bytes)to be read | ||||
|     size_t len_confirmed = stream.readStringUntil('\n').toInt(); | ||||
|     if (len_confirmed > len_requested) { | ||||
|     if (len_confirmed < len_requested) { | ||||
|       DBG(len_requested - len_confirmed, "fewer bytes confirmed than requested!"); | ||||
|     } | ||||
|     sockets[mux]->sock_available = len_confirmed; | ||||
|     // ^^ Confirmed number of data bytes to be read, which may be less than requested. | ||||
|     // 0 indicates that no data can be read. | ||||
|  | ||||
|     for (size_t i=0; i<len_confirmed; i++) { | ||||
|     for (size_t i=0; i<TinyGsmMin(len_confirmed, len_requested) ; i++) { | ||||
|       uint32_t startMillis = millis(); | ||||
| #ifdef TINY_GSM_USE_HEX | ||||
|       while (stream.available() < 2) { TINY_GSM_YIELD(); } | ||||
|       while (stream.available() < 2 && (millis() - startMillis < sockets[mux]->_timeout)) { TINY_GSM_YIELD(); } | ||||
|       char buf[4] = { 0, }; | ||||
|       buf[0] = stream.read(); | ||||
|       buf[1] = stream.read(); | ||||
|       char c = strtol(buf, NULL, 16); | ||||
| #else | ||||
|       while (!stream.available()) { TINY_GSM_YIELD(); } | ||||
|       while (!stream.available() && (millis() - startMillis < sockets[mux]->_timeout)) { TINY_GSM_YIELD(); } | ||||
|       char c = stream.read(); | ||||
| #endif | ||||
|       sockets[mux]->rx.put(c); | ||||
|     } | ||||
|     waitResponse(); | ||||
|     DBG("### READ:", len_confirmed, "from", mux); | ||||
|     maintain();  // Listen for a close or other URC | ||||
|     return len_confirmed; | ||||
|     DBG("### READ:", TinyGsmMin(len_confirmed, len_requested), "from", mux); | ||||
|     return TinyGsmMin(len_confirmed, len_requested); | ||||
|   } | ||||
|  | ||||
|   size_t modemGetAvailable(uint8_t mux) { | ||||
| @@ -923,7 +803,6 @@ protected: | ||||
|     if (!result) { | ||||
|       sockets[mux]->sock_connected = modemGetConnected(mux); | ||||
|     } | ||||
|     maintain();  // Listen for a close or other URC | ||||
|     return result; | ||||
|   } | ||||
|  | ||||
| @@ -931,7 +810,6 @@ protected: | ||||
|     sendAT(GF("+CIPSTATUS="), mux); | ||||
|     int res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""), GF(",\"CLOSING\""), GF(",\"INITIAL\"")); | ||||
|     waitResponse(); | ||||
|     maintain();  // Listen for a close or other URC | ||||
|     return 1 == res; | ||||
|   } | ||||
|  | ||||
| @@ -941,16 +819,10 @@ public: | ||||
|    Utilities | ||||
|    */ | ||||
|  | ||||
|   template<typename... Args> | ||||
|   void sendAT(Args... cmd) { | ||||
|     streamWrite("AT", cmd..., GSM_NL); | ||||
|     stream.flush(); | ||||
|     TINY_GSM_YIELD(); | ||||
|     //DBG("### AT:", cmd...); | ||||
|   } | ||||
| TINY_GSM_MODEM_STREAM_UTILITIES() | ||||
|  | ||||
|   // TODO: Optimize this! | ||||
|   uint8_t waitResponse(uint32_t timeout, String& data, | ||||
|   uint8_t waitResponse(uint32_t timeout_ms, String& data, | ||||
|                        GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||
|   { | ||||
| @@ -1016,7 +888,7 @@ public: | ||||
|           DBG("### Closed: ", mux); | ||||
|         } | ||||
|       } | ||||
|     } while (millis() - startMillis < timeout); | ||||
|     } while (millis() - startMillis < timeout_ms); | ||||
| finish: | ||||
|     if (!index) { | ||||
|       data.trim(); | ||||
| @@ -1029,12 +901,12 @@ finish: | ||||
|     return index; | ||||
|   } | ||||
|  | ||||
|   uint8_t waitResponse(uint32_t timeout, | ||||
|   uint8_t waitResponse(uint32_t timeout_ms, | ||||
|                        GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||
|   { | ||||
|     String data; | ||||
|     return waitResponse(timeout, data, r1, r2, r3, r4, r5); | ||||
|     return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5); | ||||
|   } | ||||
|  | ||||
|   uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|   | ||||
							
								
								
									
										760
									
								
								src/TinyGsmClientSaraR4.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										760
									
								
								src/TinyGsmClientSaraR4.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,760 @@ | ||||
| /** | ||||
|  * @file       TinyGsmClientSaraR4.h | ||||
|  * @author     Volodymyr Shymanskyy | ||||
|  * @license    LGPL-3.0 | ||||
|  * @copyright  Copyright (c) 2016 Volodymyr Shymanskyy | ||||
|  * @date       Nov 2016 | ||||
|  */ | ||||
|  | ||||
| #ifndef TinyGsmClientSaraR4_h | ||||
| #define TinyGsmClientSaraR4_h | ||||
| //#pragma message("TinyGSM:  TinyGsmClientSaraR4") | ||||
|  | ||||
| //#define TINY_GSM_DEBUG Serial | ||||
|  | ||||
| #if !defined(TINY_GSM_RX_BUFFER) | ||||
|   #define TINY_GSM_RX_BUFFER 64 | ||||
| #endif | ||||
|  | ||||
| #define TINY_GSM_MUX_COUNT 7 | ||||
|  | ||||
| #include <TinyGsmCommon.h> | ||||
|  | ||||
| #define GSM_NL "\r\n" | ||||
| static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; | ||||
| static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; | ||||
| static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; | ||||
|  | ||||
| enum SimStatus { | ||||
|   SIM_ERROR = 0, | ||||
|   SIM_READY = 1, | ||||
|   SIM_LOCKED = 2, | ||||
| }; | ||||
|  | ||||
| enum RegStatus { | ||||
|   REG_UNREGISTERED = 0, | ||||
|   REG_SEARCHING    = 2, | ||||
|   REG_DENIED       = 3, | ||||
|   REG_OK_HOME      = 1, | ||||
|   REG_OK_ROAMING   = 5, | ||||
|   REG_UNKNOWN      = 4, | ||||
| }; | ||||
|  | ||||
|  | ||||
| class TinyGsmSaraR4 | ||||
| { | ||||
|  | ||||
| public: | ||||
|  | ||||
| class GsmClient : public Client | ||||
| { | ||||
|   friend class TinyGsmSaraR4; | ||||
|   typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo; | ||||
|  | ||||
| public: | ||||
|   GsmClient() {} | ||||
|  | ||||
|   GsmClient(TinyGsmSaraR4& modem, uint8_t mux = 0) { | ||||
|     init(&modem, mux); | ||||
|   } | ||||
|  | ||||
|   bool init(TinyGsmSaraR4* modem, uint8_t mux = 0) { | ||||
|     this->at = modem; | ||||
|     this->mux = mux; | ||||
|     sock_available = 0; | ||||
|     prev_check = 0; | ||||
|     sock_connected = false; | ||||
|     got_data = false; | ||||
|  | ||||
|     at->sockets[mux] = this; | ||||
|  | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
| public: | ||||
|   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||
|     stop(); | ||||
|     TINY_GSM_YIELD(); | ||||
|     rx.clear(); | ||||
|  | ||||
|     uint8_t oldMux = mux; | ||||
|     sock_connected = at->modemConnect(host, port, &mux, false, timeout_s); | ||||
|     if (mux != oldMux) { | ||||
|         DBG("WARNING:  Mux number changed from", oldMux, "to", mux); | ||||
|         at->sockets[oldMux] = NULL; | ||||
|     } | ||||
|     at->sockets[mux] = this; | ||||
|     at->maintain(); | ||||
|  | ||||
|     return sock_connected; | ||||
|   } | ||||
|  | ||||
| TINY_GSM_CLIENT_CONNECT_OVERLOADS() | ||||
|  | ||||
|   virtual void stop() { | ||||
|     TINY_GSM_YIELD(); | ||||
|     // Read and dump anything remaining in the modem's internal buffer. | ||||
|     // The socket will appear open in response to connected() even after it | ||||
|     // closes until all data is read from the buffer. | ||||
|     // Doing it this way allows the external mcu to find and get all of the data | ||||
|     // that it wants from the socket even if it was closed externally. | ||||
|     rx.clear(); | ||||
|     at->maintain(); | ||||
|     while (sock_available > 0) { | ||||
|       at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux); | ||||
|       rx.clear(); | ||||
|       at->maintain(); | ||||
|     } | ||||
|     at->modemDisconnect(mux); | ||||
|   } | ||||
|  | ||||
| TINY_GSM_CLIENT_WRITE() | ||||
|  | ||||
| TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK() | ||||
|  | ||||
| TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK() | ||||
|  | ||||
| TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() | ||||
|  | ||||
|   /* | ||||
|    * Extended API | ||||
|    */ | ||||
|  | ||||
|   String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; | ||||
|  | ||||
| private: | ||||
|   TinyGsmSaraR4*   at; | ||||
|   uint8_t         mux; | ||||
|   uint16_t        sock_available; | ||||
|   uint32_t        prev_check; | ||||
|   bool            sock_connected; | ||||
|   bool            got_data; | ||||
|   RxFifo          rx; | ||||
| }; | ||||
|  | ||||
|  | ||||
| class GsmClientSecure : public GsmClient | ||||
| { | ||||
| public: | ||||
|   GsmClientSecure() {} | ||||
|  | ||||
|   GsmClientSecure(TinyGsmSaraR4& modem, uint8_t mux = 1) | ||||
|     : GsmClient(modem, mux) | ||||
|   {} | ||||
|  | ||||
| public: | ||||
|   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||
|     stop(); | ||||
|     TINY_GSM_YIELD(); | ||||
|     rx.clear(); | ||||
|     uint8_t oldMux = mux; | ||||
|     sock_connected = at->modemConnect(host, port, &mux, true, timeout_s); | ||||
|     if (mux != oldMux) { | ||||
|         DBG("WARNING:  Mux number changed from", oldMux, "to", mux); | ||||
|         at->sockets[oldMux] = NULL; | ||||
|     } | ||||
|     at->sockets[mux] = this; | ||||
|     at->maintain(); | ||||
|     return sock_connected; | ||||
|   } | ||||
| }; | ||||
|  | ||||
|  | ||||
| public: | ||||
|  | ||||
|   TinyGsmSaraR4(Stream& stream) | ||||
|     : stream(stream) | ||||
|   { | ||||
|     memset(sockets, 0, sizeof(sockets)); | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    * Basic functions | ||||
|    */ | ||||
|  | ||||
|   bool begin(const char* pin = NULL) { | ||||
|     return init(pin); | ||||
|   } | ||||
|  | ||||
|   bool init(const char* pin = NULL) { | ||||
|     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); | ||||
|     if (!testAT()) { | ||||
|       return false; | ||||
|     } | ||||
|     sendAT(GF("E0"));   // Echo Off | ||||
|     if (waitResponse() != 1) { | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
| #ifdef TINY_GSM_DEBUG | ||||
|     sendAT(GF("+CMEE=2"));  // turn on verbose error codes | ||||
| #else | ||||
|     sendAT(GF("+CMEE=0"));  // turn off error codes | ||||
| #endif | ||||
|     waitResponse(); | ||||
|  | ||||
|     getModemName(); | ||||
|  | ||||
|     int ret = getSimStatus(); | ||||
|     // if the sim isn't ready and a pin has been provided, try to unlock the sim | ||||
|     if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) { | ||||
|       simUnlock(pin); | ||||
|       return (getSimStatus() == SIM_READY); | ||||
|     } | ||||
|     // if the sim is ready, or it's locked but no pin has been provided, return true | ||||
|     else { | ||||
|       return (ret == SIM_READY || ret == SIM_LOCKED); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   String getModemName() { | ||||
|     sendAT(GF("+CGMI")); | ||||
|     String res1; | ||||
|     if (waitResponse(1000L, res1) != 1) { | ||||
|       return "u-blox Cellular Modem"; | ||||
|     } | ||||
|     res1.replace(GSM_NL "OK" GSM_NL, ""); | ||||
|     res1.trim(); | ||||
|  | ||||
|     sendAT(GF("+GMM")); | ||||
|     String res2; | ||||
|     if (waitResponse(1000L, res2) != 1) { | ||||
|       return "u-blox Cellular Modem"; | ||||
|     } | ||||
|     res2.replace(GSM_NL "OK" GSM_NL, ""); | ||||
|     res2.trim(); | ||||
|  | ||||
|     String name = res1 + String(' ') + res2; | ||||
|     DBG("### Modem:", name); | ||||
|     if (!name.startsWith("u-blox SARA-R4") && !name.startsWith("u-blox SARA-N4")) { | ||||
|       DBG("### WARNING:  You are using the wrong TinyGSM modem!"); | ||||
|     } | ||||
|  | ||||
|     return name; | ||||
|   } | ||||
|  | ||||
| TINY_GSM_MODEM_SET_BAUD_IPR() | ||||
|  | ||||
| TINY_GSM_MODEM_TEST_AT() | ||||
|  | ||||
| TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS() | ||||
|  | ||||
|   bool factoryDefault() { | ||||
|     sendAT(GF("&F"));  // Resets the current profile, other NVM not affected | ||||
|     return waitResponse() == 1; | ||||
|   } | ||||
|  | ||||
| TINY_GSM_MODEM_GET_INFO_ATI() | ||||
|  | ||||
|   bool hasSSL() { | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   bool hasWifi() { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   bool hasGPRS() { | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    * Power functions | ||||
|    */ | ||||
|  | ||||
|   bool restart() { | ||||
|     if (!testAT()) { | ||||
|       return false; | ||||
|     } | ||||
|     sendAT(GF("+CFUN=15")); | ||||
|     if (waitResponse(10000L) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     delay(3000);  // TODO:  Verify delay timing here | ||||
|     return init(); | ||||
|   } | ||||
|  | ||||
|   bool poweroff() { | ||||
|     sendAT(GF("+CPWROFF")); | ||||
|     return waitResponse(40000L) == 1; | ||||
|   } | ||||
|  | ||||
|   bool radioOff() { | ||||
|     sendAT(GF("+CFUN=0")); | ||||
|     if (waitResponse(10000L) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     delay(3000); | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   bool sleepEnable(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED; | ||||
|  | ||||
|   /* | ||||
|    * SIM card functions | ||||
|    */ | ||||
|  | ||||
| TINY_GSM_MODEM_SIM_UNLOCK_CPIN() | ||||
|  | ||||
| TINY_GSM_MODEM_GET_SIMCCID_CCID() | ||||
|  | ||||
|   String getIMEI() { | ||||
|     sendAT(GF("+CGSN")); | ||||
|     if (waitResponse(GF(GSM_NL)) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     String res = stream.readStringUntil('\n'); | ||||
|     waitResponse(); | ||||
|     res.trim(); | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout_ms; ) { | ||||
|       sendAT(GF("+CPIN?")); | ||||
|       if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { | ||||
|         delay(1000); | ||||
|         continue; | ||||
|       } | ||||
|       int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"), GF("NOT INSERTED")); | ||||
|       waitResponse(); | ||||
|       switch (status) { | ||||
|         case 2: | ||||
|         case 3:  return SIM_LOCKED; | ||||
|         case 1:  return SIM_READY; | ||||
|         default: return SIM_ERROR; | ||||
|       } | ||||
|     } | ||||
|     return SIM_ERROR; | ||||
|   } | ||||
|  | ||||
|  | ||||
| TINY_GSM_MODEM_GET_REGISTRATION_XREG(CEREG) | ||||
|  | ||||
| TINY_GSM_MODEM_GET_OPERATOR_COPS() | ||||
|  | ||||
|   /* | ||||
|    * Generic network functions | ||||
|    */ | ||||
|  | ||||
| TINY_GSM_MODEM_GET_CSQ() | ||||
|  | ||||
|   bool isNetworkConnected() { | ||||
|     RegStatus s = getRegistrationStatus(); | ||||
|     if (s == REG_OK_HOME || s == REG_OK_ROAMING) | ||||
|       return true; | ||||
|     else if (s == REG_UNKNOWN)  // for some reason, it can hang at unknown.. | ||||
|       return isGprsConnected(); | ||||
|     else return false; | ||||
|   } | ||||
|  | ||||
| TINY_GSM_MODEM_WAIT_FOR_NETWORK() | ||||
|  | ||||
|   bool setURAT( uint8_t urat ) { | ||||
|     // AT+URAT=<SelectedAcT>[,<PreferredAct>[,<2ndPreferredAct>]] | ||||
|  | ||||
|     sendAT(GF("+COPS=2"));        // Deregister from network | ||||
|     if (waitResponse() != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     sendAT(GF("+URAT="), urat);  // Radio Access Technology (RAT) selection | ||||
|     if (waitResponse() != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     sendAT(GF("+COPS=0"));        // Auto-register to the network | ||||
|     if (waitResponse() != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     return restart(); | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    * GPRS functions | ||||
|    */ | ||||
|  | ||||
|   bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { | ||||
|     gprsDisconnect(); | ||||
|  | ||||
|     sendAT(GF("+CGATT=1"));  // attach to GPRS | ||||
|     if (waitResponse(360000L) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     // Using CGDCONT sets up an "external" PCP context, i.e. a data connection | ||||
|     // using the external IP stack (e.g. Windows dial up) and PPP link over the | ||||
|     // serial interface.  This is the only command set supported by the LTE-M | ||||
|     // and LTE NB-IoT modules (SARA-R4xx, SARA-N4xx) | ||||
|  | ||||
|     if (user && strlen(user) > 0) { | ||||
|       sendAT(GF("+CGAUTH=1,0,\""), user, GF("\",\""), pwd, '"');  // Set the authentication | ||||
|       waitResponse(); | ||||
|     } | ||||
|  | ||||
|     sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"');  // Define PDP context 1 | ||||
|     waitResponse(); | ||||
|  | ||||
|     sendAT(GF("+CGACT=1,1"));  // activate PDP profile/context 1 | ||||
|     if (waitResponse(150000L) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   bool gprsDisconnect() { | ||||
|     sendAT(GF("+CGACT=1,0"));  // Deactivate PDP context 1 | ||||
|     if (waitResponse(40000L) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     sendAT(GF("+CGATT=0"));  // detach from GPRS | ||||
|     if (waitResponse(360000L) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
| TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() | ||||
|  | ||||
|   /* | ||||
|    * IP Address functions | ||||
|    */ | ||||
|  | ||||
|   String getLocalIP() { | ||||
|     sendAT(GF("+CGPADDR")); | ||||
|     if (waitResponse(GF(GSM_NL "+CGPADDR:")) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     streamSkipUntil(',');  // Skip context id | ||||
|     String res = stream.readStringUntil('\r'); | ||||
|     if (waitResponse() != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   IPAddress localIP() { | ||||
|     return TinyGsmIpFromString(getLocalIP()); | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    * Phone Call functions | ||||
|    */ | ||||
|  | ||||
|   bool setGsmBusy(bool busy = true) TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|  | ||||
|   bool callAnswer() TINY_GSM_ATTR_NOT_IMPLEMENTED; | ||||
|  | ||||
|   bool callNumber(const String& number) TINY_GSM_ATTR_NOT_IMPLEMENTED; | ||||
|  | ||||
|   bool callHangup() TINY_GSM_ATTR_NOT_IMPLEMENTED; | ||||
|  | ||||
|   /* | ||||
|    * Messaging functions | ||||
|    */ | ||||
|  | ||||
|   String sendUSSD(const String& code) TINY_GSM_ATTR_NOT_IMPLEMENTED; | ||||
|  | ||||
|   bool sendSMS(const String& number, const String& text) { | ||||
|     sendAT(GF("+CSCS=\"GSM\""));  // Set GSM default alphabet | ||||
|     waitResponse(); | ||||
|     sendAT(GF("+CMGF=1"));  // Set preferred message format to text mode | ||||
|     waitResponse(); | ||||
|     sendAT(GF("+CMGS=\""), number, GF("\""));  // set the phone number | ||||
|     if (waitResponse(GF(">")) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     stream.print(text);  // Actually send the message | ||||
|     stream.write((char)0x1A); | ||||
|     stream.flush(); | ||||
|     return waitResponse(60000L) == 1; | ||||
|   } | ||||
|  | ||||
|   bool sendSMS_UTF16(const String& number, const void* text, size_t len) TINY_GSM_ATTR_NOT_IMPLEMENTED; | ||||
|  | ||||
|  | ||||
|   /* | ||||
|    * Location functions | ||||
|    */ | ||||
|  | ||||
|   String getGsmLocation() { | ||||
|     sendAT(GF("+ULOC=2,3,0,120,1")); | ||||
|     if (waitResponse(30000L, GF(GSM_NL "+UULOC:")) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     String res = stream.readStringUntil('\n'); | ||||
|     waitResponse(); | ||||
|     res.trim(); | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    * Battery & temperature functions | ||||
|    */ | ||||
|  | ||||
|   uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|  | ||||
|   int8_t getBattPercent() { | ||||
|     sendAT(GF("+CIND?")); | ||||
|     if (waitResponse(GF(GSM_NL "+CIND:")) != 1) { | ||||
|       return 0; | ||||
|     } | ||||
|  | ||||
|     int8_t res = stream.readStringUntil(',').toInt(); | ||||
|     int8_t percent = res*20;  // return is 0-5 | ||||
|     // Wait for final OK | ||||
|     waitResponse(); | ||||
|     return percent; | ||||
|   } | ||||
|  | ||||
|   uint8_t getBattChargeState() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|  | ||||
|   bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) { | ||||
|     percent = getBattPercent(); | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   float getTemperature() { | ||||
|     // First make sure the temperature is set to be in celsius | ||||
|     sendAT(GF("+UTEMP=0"));  // Would use 1 for Fahrenheit | ||||
|     if (waitResponse() != 1) { | ||||
|       return (float)-9999; | ||||
|     } | ||||
|     sendAT(GF("+UTEMP?")); | ||||
|     if (waitResponse(GF(GSM_NL "+UTEMP:")) != 1) { | ||||
|       return (float)-9999; | ||||
|     } | ||||
|     streamSkipUntil(','); // Skip units (C/F) | ||||
|     int16_t res = stream.readStringUntil('\n').toInt(); | ||||
|     float temp = -9999; | ||||
|     if (res != 655355) { | ||||
|       temp = ((float)res)/10; | ||||
|     } | ||||
|     return temp; | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    * Client related functions | ||||
|    */ | ||||
|  | ||||
| protected: | ||||
|  | ||||
|   bool modemConnect(const char* host, uint16_t port, uint8_t* mux, | ||||
|                     bool ssl = false, int timeout_s = 120) | ||||
|   { | ||||
|     uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; | ||||
|     sendAT(GF("+USOCR=6"));  // create a socket | ||||
|     if (waitResponse(GF(GSM_NL "+USOCR:")) != 1) {  // reply is +USOCR: ## of socket created | ||||
|       return false; | ||||
|     } | ||||
|     *mux = stream.readStringUntil('\n').toInt(); | ||||
|     waitResponse(); | ||||
|  | ||||
|     if (ssl) { | ||||
|       sendAT(GF("+USOSEC="), *mux, ",1"); | ||||
|       waitResponse(); | ||||
|     } | ||||
|  | ||||
|     // Enable NODELAY | ||||
|     sendAT(GF("+USOSO="), *mux, GF(",6,1,1")); | ||||
|     waitResponse(); | ||||
|  | ||||
|     // Enable KEEPALIVE, 30 sec | ||||
|     //sendAT(GF("+USOSO="), *mux, GF(",6,2,30000")); | ||||
|     //waitResponse(); | ||||
|  | ||||
|     // connect on the allocated socket | ||||
|     sendAT(GF("+USOCO="), *mux, ",\"", host, "\",", port); | ||||
|     int rsp = waitResponse(timeout_ms); | ||||
|     return (1 == rsp); | ||||
|   } | ||||
|  | ||||
|   bool modemDisconnect(uint8_t mux) { | ||||
|     TINY_GSM_YIELD(); | ||||
|     if (!modemGetConnected(mux)) { | ||||
|       sockets[mux]->sock_connected = false; | ||||
|       return true; | ||||
|     } | ||||
|     sendAT(GF("+USOCL="), mux); | ||||
|     return 1 == waitResponse(120000L);  // can take up to 120s to get a response | ||||
|   } | ||||
|  | ||||
|   int16_t modemSend(const void* buff, size_t len, uint8_t mux) { | ||||
|     sendAT(GF("+USOWR="), mux, ',', len); | ||||
|     if (waitResponse(GF("@")) != 1) { | ||||
|       return 0; | ||||
|     } | ||||
|     // 50ms delay, see AT manual section 25.10.4 | ||||
|     delay(50); | ||||
|     stream.write((uint8_t*)buff, len); | ||||
|     stream.flush(); | ||||
|     if (waitResponse(GF(GSM_NL "+USOWR:")) != 1) { | ||||
|       return 0; | ||||
|     } | ||||
|     streamSkipUntil(','); // Skip mux | ||||
|     int sent = stream.readStringUntil('\n').toInt(); | ||||
|     waitResponse();  // sends back OK after the confirmation of number sent | ||||
|     return sent; | ||||
|   } | ||||
|  | ||||
|   size_t modemRead(size_t size, uint8_t mux) { | ||||
|     sendAT(GF("+USORD="), mux, ',', size); | ||||
|     if (waitResponse(GF(GSM_NL "+USORD:")) != 1) { | ||||
|       return 0; | ||||
|     } | ||||
|     streamSkipUntil(','); // Skip mux | ||||
|     size_t len = stream.readStringUntil(',').toInt(); | ||||
|     sockets[mux]->sock_available = len; | ||||
|     streamSkipUntil('\"'); | ||||
|  | ||||
|     for (size_t i=0; i<len; i++) { | ||||
|       TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT | ||||
|     } | ||||
|     streamSkipUntil('\"'); | ||||
|     waitResponse(); | ||||
|     DBG("### READ:", len, "from", mux); | ||||
|     return len; | ||||
|   } | ||||
|  | ||||
|   size_t modemGetAvailable(uint8_t mux) { | ||||
|     // NOTE:  Querying a closed socket gives an error "operation not allowed" | ||||
|     sendAT(GF("+USORD="), mux, ",0"); | ||||
|     size_t result = 0; | ||||
|     uint8_t res = waitResponse(GF(GSM_NL "+USORD:")); | ||||
|     // Will give error "operation not allowed" when attempting to read a socket | ||||
|     // that you have already told to close | ||||
|     if (res == 1) { | ||||
|       streamSkipUntil(','); // Skip mux | ||||
|       result = stream.readStringUntil('\n').toInt(); | ||||
|       // if (result) DBG("### DATA AVAILABLE:", result, "on", mux); | ||||
|       waitResponse(); | ||||
|     } | ||||
|     if (!result && res != 2 && res != 3) {  // Don't check modemGetConnected after an error | ||||
|       sockets[mux]->sock_connected = modemGetConnected(mux); | ||||
|     } | ||||
|     return result; | ||||
|   } | ||||
|  | ||||
|   bool modemGetConnected(uint8_t mux) { | ||||
|     // NOTE:  Querying a closed socket gives an error "operation not allowed" | ||||
|     sendAT(GF("+USOCTL="), mux, ",10"); | ||||
|     uint8_t res = waitResponse(GF(GSM_NL "+USOCTL:")); | ||||
|     if (res != 1) | ||||
|       return false; | ||||
|  | ||||
|     streamSkipUntil(','); // Skip mux | ||||
|     streamSkipUntil(','); // Skip type | ||||
|     int result = stream.readStringUntil('\n').toInt(); | ||||
|     // 0: the socket is in INACTIVE status (it corresponds to CLOSED status | ||||
|     // defined in RFC793 "TCP Protocol Specification" [112]) | ||||
|     // 1: the socket is in LISTEN status | ||||
|     // 2: the socket is in SYN_SENT status | ||||
|     // 3: the socket is in SYN_RCVD status | ||||
|     // 4: the socket is in ESTABILISHED status | ||||
|     // 5: the socket is in FIN_WAIT_1 status | ||||
|     // 6: the socket is in FIN_WAIT_2 status | ||||
|     // 7: the sokcet is in CLOSE_WAIT status | ||||
|     // 8: the socket is in CLOSING status | ||||
|     // 9: the socket is in LAST_ACK status | ||||
|     // 10: the socket is in TIME_WAIT status | ||||
|     waitResponse(); | ||||
|     return (result != 0); | ||||
|   } | ||||
|  | ||||
| public: | ||||
|  | ||||
|   /* | ||||
|    Utilities | ||||
|    */ | ||||
|  | ||||
| TINY_GSM_MODEM_STREAM_UTILITIES() | ||||
|  | ||||
|   // TODO: Optimize this! | ||||
|   uint8_t waitResponse(uint32_t timeout_ms, String& data, | ||||
|                        GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|                        GsmConstStr r3=GFP(GSM_CME_ERROR), GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||
|   { | ||||
|     /*String r1s(r1); r1s.trim(); | ||||
|     String r2s(r2); r2s.trim(); | ||||
|     String r3s(r3); r3s.trim(); | ||||
|     String r4s(r4); r4s.trim(); | ||||
|     String r5s(r5); r5s.trim(); | ||||
|     DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ | ||||
|     data.reserve(64); | ||||
|     int index = 0; | ||||
|     unsigned long startMillis = millis(); | ||||
|     do { | ||||
|       TINY_GSM_YIELD(); | ||||
|       while (stream.available() > 0) { | ||||
|         int a = stream.read(); | ||||
|         if (a <= 0) continue; // Skip 0x00 bytes, just in case | ||||
|         data += (char)a; | ||||
|         if (r1 && data.endsWith(r1)) { | ||||
|           index = 1; | ||||
|           goto finish; | ||||
|         } else if (r2 && data.endsWith(r2)) { | ||||
|           index = 2; | ||||
|           goto finish; | ||||
|         } else if (r3 && data.endsWith(r3)) { | ||||
|           index = 3; | ||||
|           goto finish; | ||||
|         } else if (r4 && data.endsWith(r4)) { | ||||
|           index = 4; | ||||
|           goto finish; | ||||
|         } else if (r5 && data.endsWith(r5)) { | ||||
|           index = 5; | ||||
|           goto finish; | ||||
|         } else if (data.endsWith(GF(GSM_NL "+UUSORD:"))) { | ||||
|           int mux = stream.readStringUntil(',').toInt(); | ||||
|           int len = stream.readStringUntil('\n').toInt(); | ||||
|           if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { | ||||
|             sockets[mux]->got_data = true; | ||||
|             sockets[mux]->sock_available = len; | ||||
|           } | ||||
|           data = ""; | ||||
|           DBG("### URC Data Received:", len, "on", mux); | ||||
|         } else if (data.endsWith(GF(GSM_NL "+UUSOCL:"))) { | ||||
|           int mux = stream.readStringUntil('\n').toInt(); | ||||
|           if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { | ||||
|             sockets[mux]->sock_connected = false; | ||||
|           } | ||||
|           data = ""; | ||||
|           DBG("### URC Sock Closed:", mux); | ||||
|         } | ||||
|       } | ||||
|     } while (millis() - startMillis < timeout_ms); | ||||
| finish: | ||||
|     if (!index) { | ||||
|       data.trim(); | ||||
|       if (data.length()) { | ||||
|         DBG("### Unhandled:", data); | ||||
|       } | ||||
|       data = ""; | ||||
|     } | ||||
|     //DBG('<', index, '>'); | ||||
|     return index; | ||||
|   } | ||||
|  | ||||
|   uint8_t waitResponse(uint32_t timeout_ms, | ||||
|                        GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|                        GsmConstStr r3=GFP(GSM_CME_ERROR), GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||
|   { | ||||
|     String data; | ||||
|     return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5); | ||||
|   } | ||||
|  | ||||
|   uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|                        GsmConstStr r3=GFP(GSM_CME_ERROR), GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||
|   { | ||||
|     return waitResponse(1000, r1, r2, r3, r4, r5); | ||||
|   } | ||||
|  | ||||
| public: | ||||
|   Stream&       stream; | ||||
|  | ||||
| protected: | ||||
|   GsmClient*    sockets[TINY_GSM_MUX_COUNT]; | ||||
| }; | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										770
									
								
								src/TinyGsmClientSequansMonarch.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										770
									
								
								src/TinyGsmClientSequansMonarch.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,770 @@ | ||||
| /** | ||||
|  * @file       TinyGsmClientSequansMonarch.h | ||||
|  * @author     Michael Krumpus | ||||
|  * @license    LGPL-3.0 | ||||
|  * @copyright  Copyright (c) 2019 Michael Krumpus | ||||
|  * @date       Jan 2019 | ||||
|  */ | ||||
|  | ||||
| #ifndef TinyGsmClientSequansMonarch_h | ||||
| #define TinyGsmClientSequansMonarch_h | ||||
|  | ||||
| //#define TINY_GSM_DEBUG Serial | ||||
| //#define TINY_GSM_USE_HEX | ||||
|  | ||||
| #if !defined(TINY_GSM_RX_BUFFER) | ||||
|   #define TINY_GSM_RX_BUFFER 64 | ||||
| #endif | ||||
|  | ||||
| #define TINY_GSM_MUX_COUNT 6 | ||||
|  | ||||
| #include <TinyGsmCommon.h> | ||||
|  | ||||
| #define GSM_NL "\r\n" | ||||
| static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; | ||||
| static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; | ||||
|  | ||||
| enum SimStatus { | ||||
|   SIM_ERROR = 0, | ||||
|   SIM_READY = 1, | ||||
|   SIM_LOCKED = 2, | ||||
| }; | ||||
|  | ||||
| enum RegStatus { | ||||
|   REG_UNREGISTERED = 0, | ||||
|   REG_SEARCHING    = 2, | ||||
|   REG_DENIED       = 3, | ||||
|   REG_OK_HOME      = 1, | ||||
|   REG_OK_ROAMING   = 5, | ||||
|   REG_UNKNOWN      = 4, | ||||
| }; | ||||
|  | ||||
| enum SocketStatus { | ||||
|   SOCK_CLOSED                 = 0, | ||||
|   SOCK_ACTIVE_DATA            = 1, | ||||
|   SOCK_SUSPENDED              = 2, | ||||
|   SOCK_SUSPENDED_PENDING_DATA = 3, | ||||
|   SOCK_LISTENING              = 4, | ||||
|   SOCK_INCOMING               = 5, | ||||
|   SOCK_OPENING                = 6, | ||||
| }; | ||||
|  | ||||
|  | ||||
| class TinyGsmSequansMonarch | ||||
| { | ||||
|  | ||||
| public: | ||||
|  | ||||
| class GsmClient : public Client | ||||
| { | ||||
|   friend class TinyGsmSequansMonarch; | ||||
|   typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo; | ||||
|  | ||||
| public: | ||||
|   GsmClient() {} | ||||
|  | ||||
|   GsmClient(TinyGsmSequansMonarch& modem, uint8_t mux = 1) { | ||||
|     init(&modem, mux); | ||||
|   } | ||||
|  | ||||
|   bool init(TinyGsmSequansMonarch* modem, uint8_t mux = 1) { | ||||
|     this->at = modem; | ||||
|     this->mux = mux; | ||||
|     sock_available = 0; | ||||
|     prev_check = 0; | ||||
|     sock_connected = false; | ||||
|     got_data = false; | ||||
|  | ||||
|     // adjust for zero indexed socket array vs Sequans' 1 indexed mux numbers | ||||
|     // using modulus will force 6 back to 0 | ||||
|     at->sockets[mux % TINY_GSM_MUX_COUNT] = this; | ||||
|  | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
| public: | ||||
|   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||
|     if (sock_connected) stop(); | ||||
|     TINY_GSM_YIELD(); | ||||
|     rx.clear(); | ||||
|     sock_connected = at->modemConnect(host, port, mux, false, timeout_s); | ||||
|     return sock_connected; | ||||
|   } | ||||
|  | ||||
| TINY_GSM_CLIENT_CONNECT_OVERLOADS() | ||||
|  | ||||
|   virtual void stop() { | ||||
|     TINY_GSM_YIELD(); | ||||
|     // Read and dump anything remaining in the modem's internal buffer. | ||||
|     // The socket will appear open in response to connected() even after it | ||||
|     // closes until all data is read from the buffer. | ||||
|     // Doing it this way allows the external mcu to find and get all of the data | ||||
|     // that it wants from the socket even if it was closed externally. | ||||
|     rx.clear(); | ||||
|     at->maintain(); | ||||
|     while (sock_available > 0) { | ||||
|       at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux); | ||||
|       rx.clear(); | ||||
|       at->maintain(); | ||||
|     } | ||||
|     at->sendAT(GF("+SQNSH="), mux); | ||||
|     sock_connected = false; | ||||
|     at->waitResponse(); | ||||
|   } | ||||
|  | ||||
| TINY_GSM_CLIENT_WRITE() | ||||
|  | ||||
| TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK() | ||||
|  | ||||
| TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK() | ||||
|  | ||||
| TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() | ||||
|  | ||||
|   /* | ||||
|    * Extended API | ||||
|    */ | ||||
|  | ||||
|   String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; | ||||
|  | ||||
| private: | ||||
|   TinyGsmSequansMonarch* at; | ||||
|   uint8_t         mux; | ||||
|   uint16_t        sock_available; | ||||
|   uint32_t        prev_check; | ||||
|   bool            sock_connected; | ||||
|   bool            got_data; | ||||
|   RxFifo          rx; | ||||
| }; | ||||
|  | ||||
|  | ||||
| class GsmClientSecure : public GsmClient | ||||
| { | ||||
| public: | ||||
|   GsmClientSecure() {} | ||||
|  | ||||
|   GsmClientSecure(TinyGsmSequansMonarch& modem, uint8_t mux = 1) | ||||
|     : GsmClient(modem, mux) | ||||
|   {} | ||||
|  | ||||
| protected: | ||||
|   bool          strictSSL = false; | ||||
|  | ||||
| public: | ||||
|   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||
|     stop(); | ||||
|     TINY_GSM_YIELD(); | ||||
|     rx.clear(); | ||||
|  | ||||
|     // configure security profile 1 with parameters: | ||||
|     if (strictSSL) { | ||||
|       // require minimum of TLS 1.2 (3) | ||||
|       // only support cipher suite 0x3D: TLS_RSA_WITH_AES_256_CBC_SHA256 | ||||
|       // verify server certificate against imported CA certs 0 and enforce validity period (3) | ||||
|       at->sendAT(GF("+SQNSPCFG=1,3,\"0x3D\",3,0,,,\"\",\"\"")); | ||||
|     } else { | ||||
|       // use TLS 1.0 or higher (1) | ||||
|       // support wider variety of cipher suites | ||||
|       // do not verify server certificate (0) | ||||
|       at->sendAT(GF("+SQNSPCFG=1,1,\"0x2F;0x35;0x3C;0x3D\",0,,,,\"\",\"\"")); | ||||
|     } | ||||
|     if (at->waitResponse() != 1) { | ||||
|       DBG("failed to configure security profile"); | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     sock_connected = at->modemConnect(host, port, mux, true, timeout_s); | ||||
|     return sock_connected; | ||||
|   } | ||||
|  | ||||
|   void setStrictSSL(bool strict) { | ||||
|     strictSSL = strict; | ||||
|   } | ||||
|  | ||||
| }; | ||||
|  | ||||
| public: | ||||
|  | ||||
|   TinyGsmSequansMonarch(Stream& stream) | ||||
|     : stream(stream) | ||||
|   { | ||||
|     memset(sockets, 0, sizeof(sockets)); | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    * Basic functions | ||||
|    */ | ||||
|  | ||||
|   bool begin(const char* pin = NULL) { | ||||
|     return init(pin); | ||||
|   } | ||||
|  | ||||
|   bool init(const char* pin = NULL) { | ||||
|     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); | ||||
|     if (!testAT()) { | ||||
|       return false; | ||||
|     } | ||||
|     sendAT(GF("E0"));   // Echo Off | ||||
|     if (waitResponse() != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     getSimStatus(); | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   String getModemName() { | ||||
|     return "Sequans Monarch"; | ||||
|   } | ||||
|  | ||||
| TINY_GSM_MODEM_SET_BAUD_IPR() | ||||
|  | ||||
| TINY_GSM_MODEM_TEST_AT() | ||||
|  | ||||
|   void maintain() { | ||||
|     for (int mux = 1; mux <= TINY_GSM_MUX_COUNT; mux++) { | ||||
|       GsmClient* sock = sockets[mux % TINY_GSM_MUX_COUNT]; | ||||
|       if (sock && sock->got_data) { | ||||
|         sock->got_data = false; | ||||
|         sock->sock_available = modemGetAvailable(mux); | ||||
|         // modemGetConnected() always checks the state of ALL socks | ||||
|         modemGetConnected(); | ||||
|       } | ||||
|     } | ||||
|     while (stream.available()) { | ||||
|       waitResponse(15, NULL, NULL); | ||||
|   } | ||||
|   } | ||||
|  | ||||
|   bool factoryDefault() { | ||||
|     sendAT(GF("&FZE0&W"));  // Factory + Reset + Echo Off + Write | ||||
|     waitResponse(); | ||||
|     sendAT(GF("+IPR=0"));   // Auto-baud | ||||
|     waitResponse(); | ||||
|     sendAT(GF("+IFC=0,0")); // No Flow Control | ||||
|     waitResponse(); | ||||
|     sendAT(GF("+ICF=3,3")); // 8 data 0 parity 1 stop | ||||
|     waitResponse(); | ||||
|     sendAT(GF("+CSCLK=0")); // Disable Slow Clock | ||||
|     waitResponse(); | ||||
|     sendAT(GF("&W"));       // Write configuration | ||||
|     return waitResponse() == 1; | ||||
|   } | ||||
|  | ||||
| TINY_GSM_MODEM_GET_INFO_ATI() | ||||
|  | ||||
|   bool hasSSL() { | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    * Power functions | ||||
|    */ | ||||
|  | ||||
|   bool restart() { | ||||
|     if (!testAT()) { | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     sendAT(GF("+CFUN=0")); | ||||
|     int res = waitResponse(20000L, GFP(GSM_OK), GFP(GSM_ERROR), GF("+SYSSTART")) ; | ||||
|     if (res != 1 && res != 3) { | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     sendAT(GF("+CFUN=1,1")); | ||||
|     res = waitResponse(20000L, GF("+SYSSTART"), GFP(GSM_ERROR)) ; | ||||
|     if (res != 1 && res != 3) { | ||||
|       return false; | ||||
|     } | ||||
|     delay(1000); | ||||
|     return init(); | ||||
|   } | ||||
|  | ||||
|   bool poweroff() { | ||||
|     sendAT(GF("+SQNSSHDN")); | ||||
|     return waitResponse(); | ||||
|   } | ||||
|  | ||||
|   bool radioOff() { | ||||
|     sendAT(GF("+CFUN=0")); | ||||
|     if (waitResponse(10000L) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     delay(3000); | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    When power saving is enabled, UART0 interface is activated with sleep mode support. | ||||
|    Module power state is controlled by RTS0 line.  When no activity on UART, CTS line | ||||
|    will be set to OFF state (driven high level) <timeout> milliseconds (100ms to 10s, | ||||
|    default 5s) after the last sent character, then module will go to sleep mode as soon | ||||
|    as DTE set RTS line to OFF state (driver high level). | ||||
|   */ | ||||
|   bool sleepEnable(bool enable = true) { | ||||
|     sendAT(GF("+SQNIPSCFG="), enable); | ||||
|     return waitResponse() == 1; | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    * SIM card functions | ||||
|    */ | ||||
|  | ||||
| TINY_GSM_MODEM_SIM_UNLOCK_CPIN() | ||||
|  | ||||
|   String getSimCCID() { | ||||
|     sendAT(GF("+SQNCCID")); | ||||
|     if (waitResponse(GF(GSM_NL "+SQNCCID:")) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     String res = stream.readStringUntil('\n'); | ||||
|     waitResponse(); | ||||
|     res.trim(); | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
| TINY_GSM_MODEM_GET_IMEI_GSN() | ||||
|  | ||||
|   SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout_ms; ) { | ||||
|       sendAT(GF("+CPIN?")); | ||||
|       if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { | ||||
|         delay(1000); | ||||
|         continue; | ||||
|       } | ||||
|       int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"), GF("NOT INSERTED")); | ||||
|       waitResponse(); | ||||
|       switch (status) { | ||||
|       case 2: | ||||
|       case 3:  return SIM_LOCKED; | ||||
|       case 1:  return SIM_READY; | ||||
|       default: return SIM_ERROR; | ||||
|       } | ||||
|     } | ||||
|     return SIM_ERROR; | ||||
|   } | ||||
|  | ||||
| TINY_GSM_MODEM_GET_REGISTRATION_XREG(CEREG) | ||||
|  | ||||
| TINY_GSM_MODEM_GET_OPERATOR_COPS() | ||||
|  | ||||
|   /* | ||||
|    * Generic network functions | ||||
|    */ | ||||
|  | ||||
| TINY_GSM_MODEM_GET_CSQ() | ||||
|  | ||||
|   bool isNetworkConnected() { | ||||
|     RegStatus s = getRegistrationStatus(); | ||||
|     if (s == REG_OK_HOME || s == REG_OK_ROAMING) { | ||||
|       // DBG(F("connected with status:"), s); | ||||
|       return true; | ||||
|     } else { | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
| TINY_GSM_MODEM_WAIT_FOR_NETWORK() | ||||
|  | ||||
|   /* | ||||
|    * GPRS functions | ||||
|    */ | ||||
|  | ||||
|   bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { | ||||
|     gprsDisconnect(); | ||||
|  | ||||
|     // Define the PDP context (This uses context #3!) | ||||
|     sendAT(GF("+CGDCONT=3,\"IPV4V6\",\""), apn, '"'); | ||||
|     waitResponse(); | ||||
|  | ||||
|     // Set authentication | ||||
|     if (user && strlen(user) > 0) { | ||||
|       sendAT(GF("+CGAUTH=3,1,\""), user, GF("\",\""), pwd, GF("\"")); | ||||
|       waitResponse(); | ||||
|     } | ||||
|  | ||||
|     // Activate the PDP context | ||||
|     sendAT(GF("+CGACT=1,3")); | ||||
|     waitResponse(60000L); | ||||
|  | ||||
|     // Attach to GPRS | ||||
|     sendAT(GF("+CGATT=1")); | ||||
|     if (waitResponse(60000L) != 1) | ||||
|       return false; | ||||
|  | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   bool gprsDisconnect() { | ||||
|     sendAT(GF("+CGATT=0")); | ||||
|     if (waitResponse(60000L) != 1) | ||||
|       return false; | ||||
|  | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   bool isGprsConnected() { | ||||
|     sendAT(GF("+CGATT?")); | ||||
|     if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     int res = stream.readStringUntil('\n').toInt(); | ||||
|     waitResponse(); | ||||
|     if (res != 1) | ||||
|       return false; | ||||
|  | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|  | ||||
|   /* | ||||
|    * IP Address functions | ||||
|    */ | ||||
|  | ||||
|   String getLocalIP() { | ||||
|     sendAT(GF("+CGPADDR=3")); | ||||
|     if (waitResponse(10000L, GF("+CGPADDR: 3,\"")) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     String res = stream.readStringUntil('\"'); | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   IPAddress localIP() { | ||||
|     return TinyGsmIpFromString(getLocalIP()); | ||||
|   } | ||||
|  | ||||
|  | ||||
|   /* | ||||
|    * Phone Call functions | ||||
|    */ | ||||
|  | ||||
|   bool setGsmBusy(bool busy = true) TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|  | ||||
|   bool callAnswer() TINY_GSM_ATTR_NOT_IMPLEMENTED; | ||||
|  | ||||
|   bool callNumber(const String& number) TINY_GSM_ATTR_NOT_IMPLEMENTED; | ||||
|  | ||||
|   bool callHangup() TINY_GSM_ATTR_NOT_IMPLEMENTED; | ||||
|  | ||||
|   String sendUSSD(const String& code) TINY_GSM_ATTR_NOT_IMPLEMENTED; | ||||
|  | ||||
|   bool sendSMS(const String& number, const String& text) { | ||||
|     sendAT(GF("+CMGF=1")); | ||||
|     waitResponse(); | ||||
|     //Set GSM 7 bit default alphabet (3GPP TS 23.038) | ||||
|     sendAT(GF("+CSCS=\"GSM\"")); | ||||
|     waitResponse(); | ||||
|     sendAT(GF("+CMGS=\""), number, GF("\"")); | ||||
|     if (waitResponse(GF(">")) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     stream.print(text); | ||||
|     stream.write((char)0x1A); | ||||
|     stream.flush(); | ||||
|     return waitResponse(60000L) == 1; | ||||
|   } | ||||
|  | ||||
|   bool sendSMS_UTF16(const String& number, const void* text, size_t len) TINY_GSM_ATTR_NOT_IMPLEMENTED; | ||||
|  | ||||
|   /* | ||||
|    * Location functions | ||||
|    */ | ||||
|  | ||||
|   String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|  | ||||
|  | ||||
|   /* | ||||
|    * Battery & temperature functions | ||||
|    */ | ||||
|  | ||||
|   uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|   int8_t getBattPercent() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|   uint8_t getBattChargeState() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|   bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|  | ||||
|   float getTemperature() { | ||||
|     sendAT(GF("+SMDTH")); | ||||
|     if (waitResponse(10000L, GF("+SMDTH: ")) != 1) { | ||||
|       return (float)-9999; | ||||
|     } | ||||
|     String res; | ||||
|     if (waitResponse(1000L, res) != 1) { | ||||
|       return (float)-9999; | ||||
|     } | ||||
|     if (res.indexOf("ERROR") >=0) { | ||||
|       return (float)-9999; | ||||
|     } | ||||
|     return res.toFloat(); | ||||
|   } | ||||
|  | ||||
| protected: | ||||
|  | ||||
|   bool modemConnect(const char* host, uint16_t port, uint8_t mux, | ||||
|                     bool ssl = false, int timeout_s = 75) | ||||
|  { | ||||
|     int rsp; | ||||
|     unsigned long startMillis = millis(); | ||||
|     uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; | ||||
|  | ||||
|     if (ssl) { | ||||
|       // enable SSl and use security profile 1 | ||||
|       //AT+SQNSSCFG=<connId>,<enable>,<spId> | ||||
|       sendAT(GF("+SQNSSCFG="), mux, GF(",1,1")); | ||||
|       if (waitResponse() != 1) { | ||||
|         DBG("### WARNING: failed to configure secure socket"); | ||||
|         return false; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // Socket configuration | ||||
|     //AT+SQNSCFG:<connId1>, <cid1>, <pktSz1>, <maxTo1>, <connTo1>, <txTo1> | ||||
|     // <connId1> = Connection ID = mux | ||||
|     // <cid1> = PDP context ID = 3 - this is number set up above in the GprsConnect function | ||||
|     // <pktSz1> = Packet Size, used for online data mode only = 300 (default) | ||||
|     // <maxTo1> = Max timeout in seconds = 90 (default) | ||||
|     // <connTo1> = Connection timeout in hundreds of milliseconds = 600 (default) | ||||
|     // <txTo1> = Data sending timeout in hundreds of milliseconds, used for online data mode only = 50 (default) | ||||
|     sendAT(GF("+SQNSCFG="), mux, GF(",3,300,90,600,50")); | ||||
|     waitResponse(5000L); | ||||
|  | ||||
|     // Socket configuration extended | ||||
|     //AT+SQNSCFGEXT:<connId1>, <srMode1>, <recvDataMode1>, <keepalive1>, <listenAutoRsp1>, <sendDataMode1> | ||||
|     // <connId1> = Connection ID = mux | ||||
|     // <srMode1> = Send/Receive URC model = 1 - data amount mode | ||||
|     // <recvDataMode1> = Receive data mode = 0  - data as text (1 would be as hex) | ||||
|     // <keepalive1> = unused = 0 | ||||
|     // <listenAutoRsp1> = Listen auto-response mode = 0 - deactivated | ||||
|     // <sendDataMode1> = Send data mode = 0  - data as text (1 would be as hex) | ||||
|     sendAT(GF("+SQNSCFGEXT="), mux, GF(",1,0,0,0,0")); | ||||
|     waitResponse(5000L); | ||||
|  | ||||
|     // Socket dial | ||||
|     //AT+SQNSD=<connId>,<txProt>,<rPort>,<IPaddr>[,<closureType>[,<lPort>[,<connMode>[,acceptAnyRemote]]]] | ||||
|     // <connId> = Connection ID = mux | ||||
|     // <txProt> = Transmission protocol = 0 - TCP (1 for UDP) | ||||
|     // <rPort> = Remote host port to contact | ||||
|     // <IPaddr> = Any valid IP address in the format “xxx.xxx.xxx.xxx” or any host name solved with a DNS query | ||||
|     // <closureType> = Socket closure behaviour for TCP, has no effect for UDP = 0 - local port closes when remote does (default) | ||||
|     // <lPort> = UDP connection local port, has no effect for TCP connections. | ||||
|     // <connMode> = Connection mode = 1 - command mode connection | ||||
|     // <acceptAnyRemote> = Applies to UDP only | ||||
|     sendAT(GF("+SQNSD="), mux, ",0,", port, ',', GF("\""), host, GF("\""), ",0,0,1"); | ||||
|     rsp = waitResponse((timeout_ms - (millis() - startMillis)), | ||||
|                       GFP(GSM_OK), | ||||
|                       GFP(GSM_ERROR), | ||||
|                       GF("NO CARRIER" GSM_NL) | ||||
|                       ); | ||||
|  | ||||
|     // creation of socket failed immediately. | ||||
|     if (rsp != 1) return false; | ||||
|  | ||||
|     // wait until we get a good status | ||||
|     bool connected = false; | ||||
|     while (!connected && ((millis() - startMillis) < timeout_ms)) { | ||||
|       connected = modemGetConnected(mux); | ||||
|       delay(100); // socket may be in opening state | ||||
|     } | ||||
|     return connected; | ||||
|   } | ||||
|  | ||||
|  | ||||
|   int modemSend(const void* buff, size_t len, uint8_t mux) { | ||||
|     if (sockets[mux % TINY_GSM_MUX_COUNT]->sock_connected == false) { | ||||
|       DBG("### Sock closed, cannot send data!"); | ||||
|       return 0; | ||||
|     } | ||||
|  | ||||
|     sendAT(GF("+SQNSSENDEXT="), mux, ',', len); | ||||
|     waitResponse(10000L, GF(GSM_NL "> ")); | ||||
|     stream.write((uint8_t*)buff, len); | ||||
|     stream.flush(); | ||||
|     if (waitResponse() != 1) { | ||||
|       DBG("### no OK after send"); | ||||
|       return 0; | ||||
|     } | ||||
|     return len; | ||||
|  | ||||
|     // uint8_t nAttempts = 5; | ||||
|     // bool gotPrompt = false; | ||||
|     // while (nAttempts > 0 && !gotPrompt) { | ||||
|     //   sendAT(GF("+SQNSSEND="), mux); | ||||
|     //   if (waitResponse(5000, GF(GSM_NL "> ")) == 1) { | ||||
|     //     gotPrompt = true; | ||||
|     //   } | ||||
|     //   nAttempts--; | ||||
|     //   delay(50); | ||||
|     // } | ||||
|     // if (gotPrompt) { | ||||
|     //   stream.write((uint8_t*)buff, len); | ||||
|     //   stream.write((char)0x1A); | ||||
|     //   stream.flush(); | ||||
|     //   if (waitResponse() != 1) { | ||||
|     //     DBG("### no OK after send"); | ||||
|     //     return 0; | ||||
|     //   } | ||||
|     //   return len; | ||||
|     // } | ||||
|     // return 0; | ||||
|   } | ||||
|  | ||||
|  | ||||
|   size_t modemRead(size_t size, uint8_t mux) { | ||||
|     sendAT(GF("+SQNSRECV="), mux, ',', size); | ||||
|     if (waitResponse(GF("+SQNSRECV: ")) != 1) { | ||||
|       return 0; | ||||
|     } | ||||
|     streamSkipUntil(','); // Skip mux | ||||
|     size_t len = stream.readStringUntil('\n').toInt(); | ||||
|     for (size_t i=0; i<len; i++) { | ||||
|       uint32_t startMillis = millis(); \ | ||||
|       while (!stream.available() && ((millis() - startMillis) < sockets[mux % TINY_GSM_MUX_COUNT]->_timeout)) { TINY_GSM_YIELD(); } \ | ||||
|       char c = stream.read(); \ | ||||
|       sockets[mux % TINY_GSM_MUX_COUNT]->rx.put(c); | ||||
|     } | ||||
|     // DBG("### Read:", len, "from", mux); | ||||
|     waitResponse(); | ||||
|     sockets[mux % TINY_GSM_MUX_COUNT]->sock_available = modemGetAvailable(mux); | ||||
|     return len; | ||||
|   } | ||||
|  | ||||
|   size_t modemGetAvailable(uint8_t mux) { | ||||
|     sendAT(GF("+SQNSI="), mux); | ||||
|     size_t result = 0; | ||||
|     if (waitResponse(GF("+SQNSI:")) == 1) { | ||||
|       streamSkipUntil(','); // Skip mux | ||||
|       streamSkipUntil(','); // Skip total sent | ||||
|       streamSkipUntil(','); // Skip total received | ||||
|       result = stream.readStringUntil(',').toInt();  // keep data not yet read | ||||
|       waitResponse(); | ||||
|     } | ||||
|     // DBG("### Available:", result, "on", mux); | ||||
|     return result; | ||||
|   } | ||||
|  | ||||
|   bool modemGetConnected(uint8_t mux = 1) { | ||||
|     // This single command always returns the connection status of all | ||||
|     // six possible sockets. | ||||
|     sendAT(GF("+SQNSS")); | ||||
|     for (int muxNo = 1; muxNo <= TINY_GSM_MUX_COUNT; muxNo++) { | ||||
|       if (waitResponse(GFP(GSM_OK), GF(GSM_NL "+SQNSS: ")) != 2) { | ||||
|         break; | ||||
|       }; | ||||
|       uint8_t status = 0; | ||||
|       // if (stream.readStringUntil(',').toInt() != muxNo) { // check the mux no | ||||
|       //   DBG("### Warning: misaligned mux numbers!"); | ||||
|       // } | ||||
|       streamSkipUntil(',');  // skip mux [use muxNo] | ||||
|       status = stream.parseInt();  // Read the status | ||||
|       // if mux is in use, will have comma then other info after the status | ||||
|       // if not, there will be new line immediately after status | ||||
|       // streamSkipUntil('\n'); // Skip port and IP info | ||||
|       // SOCK_CLOSED                 = 0, | ||||
|       // SOCK_ACTIVE_DATA            = 1, | ||||
|       // SOCK_SUSPENDED              = 2, | ||||
|       // SOCK_SUSPENDED_PENDING_DATA = 3, | ||||
|       // SOCK_LISTENING              = 4, | ||||
|       // SOCK_INCOMING               = 5, | ||||
|       // SOCK_OPENING                = 6, | ||||
|       sockets[muxNo % TINY_GSM_MUX_COUNT]->sock_connected = \ | ||||
|         ((status != SOCK_CLOSED) && (status != SOCK_INCOMING) && (status != SOCK_OPENING)); | ||||
|     } | ||||
|     waitResponse();  // Should be an OK at the end | ||||
|     return sockets[mux % TINY_GSM_MUX_COUNT]->sock_connected; | ||||
|   } | ||||
|  | ||||
| public: | ||||
|  | ||||
|   /* | ||||
|    Utilities | ||||
|    */ | ||||
|  | ||||
| TINY_GSM_MODEM_STREAM_UTILITIES() | ||||
|  | ||||
|   // TODO: Optimize this! | ||||
|   uint8_t waitResponse(uint32_t timeout_ms, String& data, | ||||
|                        GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||
|   { | ||||
|     /*String r1s(r1); r1s.trim(); | ||||
|     String r2s(r2); r2s.trim(); | ||||
|     String r3s(r3); r3s.trim(); | ||||
|     String r4s(r4); r4s.trim(); | ||||
|     String r5s(r5); r5s.trim(); | ||||
|     DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ | ||||
|     data.reserve(64); | ||||
|     int index = 0; | ||||
|     unsigned long startMillis = millis(); | ||||
|     do { | ||||
|       TINY_GSM_YIELD(); | ||||
|       while (stream.available() > 0) { | ||||
|         int a = stream.read(); | ||||
|         if (a <= 0) continue; // Skip 0x00 bytes, just in case | ||||
|         data += (char)a; | ||||
|         if (r1 && data.endsWith(r1)) { | ||||
|           index = 1; | ||||
|           goto finish; | ||||
|         } else if (r2 && data.endsWith(r2)) { | ||||
|           index = 2; | ||||
|           goto finish; | ||||
|         } else if (r3 && data.endsWith(r3)) { | ||||
|           index = 3; | ||||
|           goto finish; | ||||
|         } else if (r4 && data.endsWith(r4)) { | ||||
|           index = 4; | ||||
|           goto finish; | ||||
|         } else if (r5 && data.endsWith(r5)) { | ||||
|           index = 5; | ||||
|           goto finish; | ||||
|         } else if (data.endsWith(GF(GSM_NL "+SQNSRING:"))) { | ||||
|           int mux = stream.readStringUntil(',').toInt(); | ||||
|           int len = stream.readStringUntil('\n').toInt(); | ||||
|           if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux % TINY_GSM_MUX_COUNT]) { | ||||
|             sockets[mux % TINY_GSM_MUX_COUNT]->got_data = true; | ||||
|             sockets[mux % TINY_GSM_MUX_COUNT]->sock_available = len; | ||||
|           } | ||||
|           data = ""; | ||||
|           DBG("### URC Data Received:", len, "on", mux); | ||||
|         } else if (data.endsWith(GF("SQNSH: "))) { | ||||
|           int mux = stream.readStringUntil('\n').toInt(); | ||||
|           if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux % TINY_GSM_MUX_COUNT]) { | ||||
|             sockets[mux % TINY_GSM_MUX_COUNT]->sock_connected = false; | ||||
|           } | ||||
|           data = ""; | ||||
|           DBG("### URC Sock Closed: ", mux); | ||||
|         } | ||||
|       } | ||||
|     } while (millis() - startMillis < timeout_ms); | ||||
| finish: | ||||
|     if (!index) { | ||||
|       data.trim(); | ||||
|       if (data.length()) { | ||||
|         DBG("### Unhandled:", data); | ||||
|       } | ||||
|       data = ""; | ||||
|     } | ||||
|     return index; | ||||
|   } | ||||
|  | ||||
|   uint8_t waitResponse(uint32_t timeout_ms, | ||||
|                        GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||
|   { | ||||
|     String data; | ||||
|     return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5); | ||||
|   } | ||||
|  | ||||
|   uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||
|   { | ||||
|     return waitResponse(1000, r1, r2, r3, r4, r5); | ||||
|   } | ||||
|  | ||||
| public: | ||||
|   Stream&       stream; | ||||
|  | ||||
| protected: | ||||
|   GsmClient*    sockets[TINY_GSM_MUX_COUNT]; | ||||
| }; | ||||
|  | ||||
| #endif | ||||
| @@ -12,6 +12,10 @@ | ||||
|  | ||||
| //#define TINY_GSM_DEBUG Serial | ||||
|  | ||||
| #if !defined(TINY_GSM_RX_BUFFER) | ||||
|   #define TINY_GSM_RX_BUFFER 64 | ||||
| #endif | ||||
|  | ||||
| #define TINY_GSM_MUX_COUNT 7 | ||||
|  | ||||
| #include <TinyGsmCommon.h> | ||||
| @@ -37,7 +41,7 @@ enum RegStatus { | ||||
| }; | ||||
|  | ||||
|  | ||||
| class TinyGsmUBLOX : public TinyGsmModem | ||||
| class TinyGsmUBLOX | ||||
| { | ||||
|  | ||||
| public: | ||||
| @@ -62,50 +66,30 @@ public: | ||||
|     sock_connected = false; | ||||
|     got_data = false; | ||||
|  | ||||
|     // at->sockets[mux] = this; | ||||
|     //  ^^ TODO: attach the socket here at init?  Or later at connect? | ||||
|     // Currently done inconsistently between modems | ||||
|     at->sockets[mux] = this; | ||||
|  | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
| public: | ||||
|   virtual int connect(const char *host, uint16_t port) { | ||||
|     if (sock_connected) { | ||||
|       stop(); | ||||
|       // If we're creating a new connection on the same client, we need to wait | ||||
|       // until the async close has finished on Cat-M modems. | ||||
|       // After close has completed, the +UUSOCL should appear. | ||||
|       if (at->isCatM) { | ||||
|         DBG("Waiting for +UUSOCL URC on", mux); | ||||
|         for (unsigned long start = millis(); millis() - start < 120000L; ) { | ||||
|           at->maintain(); | ||||
|           if (!sock_connected) break; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||
|     stop(); | ||||
|     TINY_GSM_YIELD(); | ||||
|     rx.clear(); | ||||
|     // sock_connected = at->modemConnect(host, port, mux); | ||||
|     sock_connected = at->modemConnect(host, port, &mux); | ||||
|  | ||||
|     uint8_t oldMux = mux; | ||||
|     sock_connected = at->modemConnect(host, port, &mux, false, timeout_s); | ||||
|     if (mux != oldMux) { | ||||
|         DBG("WARNING:  Mux number changed from", oldMux, "to", mux); | ||||
|         at->sockets[oldMux] = NULL; | ||||
|     } | ||||
|     at->sockets[mux] = this; | ||||
|     // ^^ TODO: attach the socet after attempting connection or above at init? | ||||
|     // Currently done inconsistently between modems | ||||
|     at->maintain(); | ||||
|  | ||||
|     return sock_connected; | ||||
|   } | ||||
|  | ||||
|   virtual int connect(IPAddress ip, uint16_t port) { | ||||
|     String host; host.reserve(16); | ||||
|     host += ip[0]; | ||||
|     host += "."; | ||||
|     host += ip[1]; | ||||
|     host += "."; | ||||
|     host += ip[2]; | ||||
|     host += "."; | ||||
|     host += ip[3]; | ||||
|     return connect(host.c_str(), port); | ||||
|   } | ||||
| TINY_GSM_CLIENT_CONNECT_OVERLOADS() | ||||
|  | ||||
|   virtual void stop() { | ||||
|     TINY_GSM_YIELD(); | ||||
| @@ -122,89 +106,15 @@ public: | ||||
|       at->maintain(); | ||||
|     } | ||||
|     at->modemDisconnect(mux); | ||||
|     // We don't actually know if the CatM modem has finished closing because | ||||
|     // we're using an "asynchronous" close | ||||
|     if (!at->isCatM) sock_connected = false; | ||||
|   } | ||||
|  | ||||
|   virtual size_t write(const uint8_t *buf, size_t size) { | ||||
|     TINY_GSM_YIELD(); | ||||
|     at->maintain(); | ||||
|     return at->modemSend(buf, size, mux); | ||||
|   } | ||||
| TINY_GSM_CLIENT_WRITE() ; | ||||
|  | ||||
|   virtual size_t write(uint8_t c) { | ||||
|     return write(&c, 1); | ||||
|   } | ||||
| TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK() ; | ||||
|  | ||||
|   virtual size_t write(const char *str) { | ||||
|     if (str == NULL) return 0; | ||||
|     return write((const uint8_t *)str, strlen(str)); | ||||
|   } | ||||
| TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK() ; | ||||
|  | ||||
|   virtual int available() { | ||||
|     TINY_GSM_YIELD(); | ||||
|     if (!rx.size()) { | ||||
|       // Workaround: sometimes SARA R410 forgets to notify about data arrival. | ||||
|       // TODO: Currently we ping the module periodically, | ||||
|       // but maybe there's a better indicator that we need to poll | ||||
|       if (millis() - prev_check > 250) { | ||||
|         got_data = true; | ||||
|         prev_check = millis(); | ||||
|       } | ||||
|       at->maintain(); | ||||
|     } | ||||
|     return rx.size() + sock_available; | ||||
|   } | ||||
|  | ||||
|   virtual int read(uint8_t *buf, size_t size) { | ||||
|     TINY_GSM_YIELD(); | ||||
|     at->maintain(); | ||||
|     size_t cnt = 0; | ||||
|     while (cnt < size) { | ||||
|       size_t chunk = TinyGsmMin(size-cnt, rx.size()); | ||||
|       if (chunk > 0) { | ||||
|         rx.get(buf, chunk); | ||||
|         buf += chunk; | ||||
|         cnt += chunk; | ||||
|         continue; | ||||
|       } | ||||
|       // Workaround: sometimes SARA R410 forgets to notify about data arrival. | ||||
|       // TODO: Currently we ping the module periodically, | ||||
|       // but maybe there's a better indicator that we need to poll | ||||
|       if (millis() - prev_check > 250) { | ||||
|         got_data = true; | ||||
|         prev_check = millis(); | ||||
|       } | ||||
|       // TODO: Read directly into user buffer? | ||||
|       at->maintain(); | ||||
|       if (sock_available > 0) { | ||||
|         at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux); | ||||
|       } else { | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|     return cnt; | ||||
|   } | ||||
|  | ||||
|   virtual int read() { | ||||
|     uint8_t c; | ||||
|     if (read(&c, 1) == 1) { | ||||
|       return c; | ||||
|     } | ||||
|     return -1; | ||||
|   } | ||||
|  | ||||
|   virtual int peek() { return -1; } //TODO | ||||
|   virtual void flush() { at->stream.flush(); } | ||||
|  | ||||
|   virtual uint8_t connected() { | ||||
|     if (available()) { | ||||
|       return true; | ||||
|     } | ||||
|     return sock_connected; | ||||
|   } | ||||
|   virtual operator bool() { return connected(); } | ||||
| TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() | ||||
|  | ||||
|   /* | ||||
|    * Extended API | ||||
| @@ -233,14 +143,17 @@ public: | ||||
|   {} | ||||
|  | ||||
| public: | ||||
|   virtual int connect(const char *host, uint16_t port) { | ||||
|   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||
|     stop(); | ||||
|     TINY_GSM_YIELD(); | ||||
|     rx.clear(); | ||||
|     // sock_connected = at->modemConnect(host, port, mux, true); | ||||
|     sock_connected = at->modemConnect(host, port, &mux, true); | ||||
|     uint8_t oldMux = mux; | ||||
|     sock_connected = at->modemConnect(host, port, &mux, true, timeout_s); | ||||
|     if (mux != oldMux) { | ||||
|         DBG("WARNING:  Mux number changed from", oldMux, "to", mux); | ||||
|         at->sockets[oldMux] = NULL; | ||||
|     } | ||||
|     at->sockets[mux] = this; | ||||
|     // TODO:  When is the socket attached? | ||||
|     at->maintain(); | ||||
|     return sock_connected; | ||||
|   } | ||||
| @@ -250,16 +163,19 @@ public: | ||||
| public: | ||||
|  | ||||
|   TinyGsmUBLOX(Stream& stream) | ||||
|     : TinyGsmModem(stream), stream(stream) | ||||
|     : stream(stream) | ||||
|   { | ||||
|     memset(sockets, 0, sizeof(sockets)); | ||||
|     isCatM = false;  // For SARA R4 and N4 series | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    * Basic functions | ||||
|    */ | ||||
|  | ||||
|   bool begin(const char* pin = NULL) { | ||||
|     return init(pin); | ||||
|   } | ||||
|  | ||||
|   bool init(const char* pin = NULL) { | ||||
|     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); | ||||
|     if (!testAT()) { | ||||
| @@ -277,14 +193,8 @@ public: | ||||
| #endif | ||||
|     waitResponse(); | ||||
|  | ||||
|     String name = getModemName(); | ||||
|     DBG(GF("### Modem:"), name); | ||||
|     if (name.startsWith("u-blox SARA-R4") or name.startsWith("u-blox SARA-N4")) { | ||||
|       isCatM = true; | ||||
|     } | ||||
|     else if (name.startsWith("u-blox SARA-N2")) { | ||||
|       DBG(GF("### SARA N2 NB-IoT modems not supported!"), name); | ||||
|     } | ||||
|     getModemName(); | ||||
|  | ||||
|     int ret = getSimStatus(); | ||||
|     // if the sim isn't ready and a pin has been provided, try to unlock the sim | ||||
|     if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) { | ||||
| @@ -314,59 +224,32 @@ public: | ||||
|     res2.replace(GSM_NL "OK" GSM_NL, ""); | ||||
|     res2.trim(); | ||||
|  | ||||
|     return res1 + String(' ') + res2; | ||||
|     String name = res1 + String(' ') + res2; | ||||
|     DBG("### Modem:", name); | ||||
|     if (name.startsWith("u-blox SARA-R4") || name.startsWith("u-blox SARA-N4")) { | ||||
|       DBG("### WARNING:  You are using the wrong TinyGSM modem!"); | ||||
|     } | ||||
|     else if (name.startsWith("u-blox SARA-N2")) { | ||||
|       DBG("### SARA N2 NB-IoT modems not supported!"); | ||||
|     } | ||||
|  | ||||
|     return name; | ||||
|   } | ||||
|  | ||||
|   void setBaud(unsigned long baud) { | ||||
|     sendAT(GF("+IPR="), baud); | ||||
|   } | ||||
| TINY_GSM_MODEM_SET_BAUD_IPR() | ||||
|  | ||||
|   bool testAT(unsigned long timeout = 10000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { | ||||
|       sendAT(GF("")); | ||||
|       if (waitResponse(200) == 1) return true; | ||||
|       delay(100); | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
| TINY_GSM_MODEM_TEST_AT() | ||||
|  | ||||
|   void maintain() { | ||||
|     for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) { | ||||
|       GsmClient* sock = sockets[mux]; | ||||
|       if (sock && sock->got_data) { | ||||
|         sock->got_data = false; | ||||
|         sock->sock_available = modemGetAvailable(mux); | ||||
|       } | ||||
|     } | ||||
|     while (stream.available()) { | ||||
|       waitResponse(15, NULL, NULL); | ||||
|     } | ||||
|   } | ||||
| TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS() | ||||
|  | ||||
|   bool factoryDefault() { | ||||
|     if (!isCatM) { | ||||
|       sendAT(GF("+UFACTORY=0,1"));  // No factory restore, erase NVM | ||||
|       waitResponse(); | ||||
|       sendAT(GF("+CFUN=16"));   // Reset | ||||
|       return waitResponse() == 1; | ||||
|     } | ||||
|     else { | ||||
|       sendAT(GF("&F"));  // Resets the current profile, other NVM not affected | ||||
|       return waitResponse() == 1; | ||||
|     } | ||||
|     sendAT(GF("+UFACTORY=0,1"));  // No factory restore, erase NVM | ||||
|     waitResponse(); | ||||
|     sendAT(GF("+CFUN=16"));   // Reset | ||||
|     return waitResponse() == 1; | ||||
|   } | ||||
|  | ||||
|   String getModemInfo() { | ||||
|     sendAT(GF("I")); | ||||
|     String res; | ||||
|     if (waitResponse(1000L, res) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     res.replace(GSM_NL "OK" GSM_NL, ""); | ||||
|     res.replace(GSM_NL, " "); | ||||
|     res.trim(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_INFO_ATI() | ||||
|  | ||||
|   bool hasSSL() { | ||||
|     return true; | ||||
| @@ -388,14 +271,7 @@ public: | ||||
|     if (!testAT()) { | ||||
|       return false; | ||||
|     } | ||||
|     if (!isCatM) | ||||
|     { | ||||
|       sendAT(GF("+CFUN=16")); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|       sendAT(GF("+CFUN=15")); | ||||
|     } | ||||
|     sendAT(GF("+CFUN=16")); | ||||
|     if (waitResponse(10000L) != 1) { | ||||
|       return false; | ||||
|     } | ||||
| @@ -423,21 +299,9 @@ public: | ||||
|    * SIM card functions | ||||
|    */ | ||||
|  | ||||
|   bool simUnlock(const char *pin) { | ||||
|     sendAT(GF("+CPIN=\""), pin, GF("\"")); | ||||
|     return waitResponse() == 1; | ||||
|   } | ||||
| TINY_GSM_MODEM_SIM_UNLOCK_CPIN() | ||||
|  | ||||
|   String getSimCCID() { | ||||
|     sendAT(GF("+CCID")); | ||||
|     if (waitResponse(GF(GSM_NL "+CCID:")) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     String res = stream.readStringUntil('\n'); | ||||
|     waitResponse(); | ||||
|     res.trim(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_SIMCCID_CCID() | ||||
|  | ||||
|   String getIMEI() { | ||||
|     sendAT(GF("+CGSN")); | ||||
| @@ -450,8 +314,8 @@ public: | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   SimStatus getSimStatus(unsigned long timeout = 10000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { | ||||
|   SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout_ms; ) { | ||||
|       sendAT(GF("+CPIN?")); | ||||
|       if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { | ||||
|         delay(1000); | ||||
| @@ -469,49 +333,15 @@ public: | ||||
|     return SIM_ERROR; | ||||
|   } | ||||
|  | ||||
|   RegStatus getRegistrationStatus() { | ||||
|     if (isCatM) {  // Check EPS registration for LTE modules | ||||
|       sendAT(GF("+CEREG?")); | ||||
|       if (waitResponse(GF(GSM_NL "+CEREG:")) != 1) { | ||||
|         return REG_UNKNOWN; | ||||
|       } | ||||
|     } | ||||
|     else { | ||||
|       sendAT(GF("+CGREG?"));  // Check GPRS registration for others | ||||
|       if (waitResponse(GF(GSM_NL "+CGREG:")) != 1) { | ||||
|         return REG_UNKNOWN; | ||||
|       } | ||||
|     } | ||||
|     streamSkipUntil(','); // Skip format (0) | ||||
|     int status = stream.readStringUntil('\n').toInt(); | ||||
|     waitResponse(); | ||||
|     return (RegStatus)status; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_REGISTRATION_XREG(CGREG) | ||||
|  | ||||
|   String getOperator() { | ||||
|     sendAT(GF("+COPS?")); | ||||
|     if (waitResponse(GF(GSM_NL "+COPS:")) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     streamSkipUntil('"'); // Skip mode and format | ||||
|     String res = stream.readStringUntil('"'); | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_OPERATOR_COPS() | ||||
|  | ||||
|   /* | ||||
|    * Generic network functions | ||||
|    */ | ||||
|  | ||||
|   int16_t getSignalQuality() { | ||||
|     sendAT(GF("+CSQ")); | ||||
|     if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) { | ||||
|       return 99; | ||||
|     } | ||||
|     int res = stream.readStringUntil(',').toInt(); | ||||
|     waitResponse(); | ||||
|     return res; | ||||
|   } | ||||
| TINY_GSM_MODEM_GET_CSQ() | ||||
|  | ||||
|   bool isNetworkConnected() { | ||||
|     RegStatus s = getRegistrationStatus(); | ||||
| @@ -522,27 +352,12 @@ public: | ||||
|     else return false; | ||||
|   } | ||||
|  | ||||
|   bool setURAT( uint8_t urat ) { | ||||
|     // AT+URAT=<SelectedAcT>[,<PreferredAct>[,<2ndPreferredAct>]] | ||||
|  | ||||
|     sendAT(GF("+COPS=2"));        // Deregister from network | ||||
|     if (waitResponse() != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     sendAT(GF("+URAT="), urat);  // Radio Access Technology (RAT) selection | ||||
|     if (waitResponse() != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     sendAT(GF("+COPS=0"));        // Auto-register to the network | ||||
|     if (waitResponse() != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     return restart(); | ||||
|   } | ||||
| TINY_GSM_MODEM_WAIT_FOR_NETWORK() | ||||
|  | ||||
|   /* | ||||
|    * GPRS functions | ||||
|    */ | ||||
|  | ||||
|   bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { | ||||
|     gprsDisconnect(); | ||||
|  | ||||
| @@ -551,131 +366,75 @@ public: | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     // Using CGDCONT sets up an "external" PCP context, i.e. a data connection | ||||
|     // using the external IP stack (e.g. Windows dial up) and PPP link over the | ||||
|     // serial interface.  This is the only command set supported by the LTE-M | ||||
|     // and LTE NB-IoT modules (SARA-R4xx, SARA-N4xx) | ||||
|  | ||||
|     // Setting up the PSD profile/PDP context with the UPSD commands sets up an | ||||
|     // "internal" PDP context, i.e. a data connection using the internal IP | ||||
|     // stack and related AT commands for sockets. This is what we're using for | ||||
|     // all of the other modules. | ||||
|     // stack and related AT commands for sockets. | ||||
|  | ||||
|     if (isCatM) { | ||||
|       if (user && strlen(user) > 0) { | ||||
|         sendAT(GF("+CGAUTH=1,0,\""), user, GF("\",\""), pwd, '"');  // Set the authentication | ||||
|         waitResponse(); | ||||
|       } | ||||
|     sendAT(GF("+UPSD=0,1,\""), apn, '"');  // Set APN for PSD profile 0 | ||||
|     waitResponse(); | ||||
|  | ||||
|       sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"');  // Define PDP context 1 | ||||
|     if (user && strlen(user) > 0) { | ||||
|       sendAT(GF("+UPSD=0,2,\""), user, '"');  // Set user for PSD profile 0 | ||||
|       waitResponse(); | ||||
|     } | ||||
|     if (pwd && strlen(pwd) > 0) { | ||||
|       sendAT(GF("+UPSD=0,3,\""), pwd, '"');  // Set password for PSD profile 0 | ||||
|       waitResponse(); | ||||
|  | ||||
|       sendAT(GF("+CGACT=1,1"));  // activate PDP profile/context 1 | ||||
|       if (waitResponse(150000L) != 1) { | ||||
|         return false; | ||||
|       } | ||||
|  | ||||
|       return true; | ||||
|     } | ||||
|  | ||||
|     else { | ||||
|       sendAT(GF("+UPSD=0,1,\""), apn, '"');  // Set APN for PSD profile 0 | ||||
|       waitResponse(); | ||||
|     sendAT(GF("+UPSD=0,7,\"0.0.0.0\"")); // Dynamic IP on PSD profile 0 | ||||
|     waitResponse(); | ||||
|  | ||||
|       if (user && strlen(user) > 0) { | ||||
|         sendAT(GF("+UPSD=0,2,\""), user, '"');  // Set user for PSD profile 0 | ||||
|         waitResponse(); | ||||
|       } | ||||
|       if (pwd && strlen(pwd) > 0) { | ||||
|         sendAT(GF("+UPSD=0,3,\""), pwd, '"');  // Set password for PSD profile 0 | ||||
|         waitResponse(); | ||||
|       } | ||||
|  | ||||
|       sendAT(GF("+UPSD=0,7,\"0.0.0.0\"")); // Dynamic IP on PSD profile 0 | ||||
|       waitResponse(); | ||||
|  | ||||
|       sendAT(GF("+UPSDA=0,3")); // Activate the PDP context associated with profile 0 | ||||
|       if (waitResponse(360000L) != 1) { | ||||
|         return false; | ||||
|       } | ||||
|  | ||||
|       sendAT(GF("+UPSND=0,8")); // Activate PSD profile 0 | ||||
|       if (waitResponse(GF(",8,1")) != 1) { | ||||
|         return false; | ||||
|       } | ||||
|       waitResponse(); | ||||
|  | ||||
|       return true; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   bool gprsDisconnect() { | ||||
|  | ||||
|     // LTE-M and NB-IoT modules do not support UPSx commands | ||||
|     if (isCatM) { | ||||
|       sendAT(GF("+CGACT=1,0"));  // Deactivate PDP context 1 | ||||
|       if (waitResponse(40000L) != 1) { | ||||
|         return false; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     else { | ||||
|       sendAT(GF("+UPSDA=0,4"));  // Deactivate the PDP context associated with profile 0 | ||||
|       if (waitResponse(360000L) != 1) | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     sendAT(GF("+CGATT=0"));  // detach from GPRS | ||||
|     if (waitResponse(360000L) != 1) | ||||
|     sendAT(GF("+UPSDA=0,3")); // Activate the PDP context associated with profile 0 | ||||
|     if (waitResponse(360000L) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     sendAT(GF("+UPSND=0,8")); // Activate PSD profile 0 | ||||
|     if (waitResponse(GF(",8,1")) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     waitResponse(); | ||||
|  | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   bool isGprsConnected() { | ||||
|     sendAT(GF("+CGATT?")); | ||||
|     if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) { | ||||
|   bool gprsDisconnect() { | ||||
|     sendAT(GF("+UPSDA=0,4"));  // Deactivate the PDP context associated with profile 0 | ||||
|     if (waitResponse(360000L) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|     int res = stream.readStringUntil('\n').toInt(); | ||||
|     waitResponse(); | ||||
|     if (res != 1) | ||||
|       return false; | ||||
|  | ||||
|     return localIP() != IPAddress(0,0,0,0); | ||||
|     sendAT(GF("+CGATT=0"));  // detach from GPRS | ||||
|     if (waitResponse(360000L) != 1) { | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
| TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() | ||||
|  | ||||
|   /* | ||||
|    * IP Address functions | ||||
|    */ | ||||
|  | ||||
|   String getLocalIP() { | ||||
|     // LTE-M and NB-IoT modules do not support UPSx commands | ||||
|     if (isCatM) { | ||||
|       sendAT(GF("+CGPADDR")); | ||||
|       if (waitResponse(GF(GSM_NL "+CGPADDR:")) != 1) { | ||||
|         return ""; | ||||
|       } | ||||
|       streamSkipUntil(',');  // Skip context id | ||||
|       String res = stream.readStringUntil('\r'); | ||||
|       if (waitResponse() != 1) { | ||||
|         return ""; | ||||
|       } | ||||
|       return res; | ||||
|     sendAT(GF("+UPSND=0,0")); | ||||
|     if (waitResponse(GF(GSM_NL "+UPSND:")) != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     else { | ||||
|       sendAT(GF("+UPSND=0,0")); | ||||
|       if (waitResponse(GF(GSM_NL "+UPSND:")) != 1) { | ||||
|         return ""; | ||||
|       } | ||||
|       streamSkipUntil(',');  // Skip PSD profile | ||||
|       streamSkipUntil('\"'); // Skip request type | ||||
|       String res = stream.readStringUntil('\"'); | ||||
|       if (waitResponse() != 1) { | ||||
|         return ""; | ||||
|       } | ||||
|       return res; | ||||
|     streamSkipUntil(',');  // Skip PSD profile | ||||
|     streamSkipUntil('\"'); // Skip request type | ||||
|     String res = stream.readStringUntil('\"'); | ||||
|     if (waitResponse() != 1) { | ||||
|       return ""; | ||||
|     } | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   IPAddress localIP() { | ||||
|     return TinyGsmIpFromString(getLocalIP()); | ||||
|   } | ||||
|  | ||||
|   /* | ||||
| @@ -730,8 +489,9 @@ public: | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    * Battery functions | ||||
|    * Battery & temperature functions | ||||
|    */ | ||||
|  | ||||
|   uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|  | ||||
|   int8_t getBattPercent() { | ||||
| @@ -745,13 +505,26 @@ public: | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   uint8_t getBattChargeState() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|  | ||||
|   bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) { | ||||
|     percent = getBattPercent(); | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   // This would only available for a small number of modules in this group (TOBY-L) | ||||
|   float getTemperature() TINY_GSM_ATTR_NOT_IMPLEMENTED; | ||||
|  | ||||
|   /* | ||||
|    * Client related functions | ||||
|    */ | ||||
|  | ||||
| protected: | ||||
|  | ||||
|   bool modemConnect(const char* host, uint16_t port, uint8_t* mux, bool ssl = false) { | ||||
|   bool modemConnect(const char* host, uint16_t port, uint8_t* mux, | ||||
|                     bool ssl = false, int timeout_s = 120) | ||||
|   { | ||||
|     uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; | ||||
|     sendAT(GF("+USOCR=6"));  // create a socket | ||||
|     if (waitResponse(GF(GSM_NL "+USOCR:")) != 1) {  // reply is +USOCR: ## of socket created | ||||
|       return false; | ||||
| @@ -776,22 +549,23 @@ protected: | ||||
|     // TODO:  Use faster "asynchronous" connection? | ||||
|     // We would have to wait for the +UUSOCO URC to verify connection | ||||
|     sendAT(GF("+USOCO="), *mux, ",\"", host, "\",", port); | ||||
|     int rsp = waitResponse(120000L); | ||||
|     int rsp = waitResponse(timeout_ms); | ||||
|     return (1 == rsp); | ||||
|   } | ||||
|  | ||||
|   bool modemDisconnect(uint8_t mux) { | ||||
|     TINY_GSM_YIELD(); | ||||
|     if (!modemGetConnected(mux)) return true; | ||||
|     if (isCatM) {  //  These modems allow a faster "asynchronous" close | ||||
|       sendAT(GF("+USOCL="), mux, GF(",1")); | ||||
|       int rsp = waitResponse(120000L); | ||||
|       return (1 == rsp);  // but it still can take up to 120s to get a response | ||||
|     if (!modemGetConnected(mux)) { | ||||
|       sockets[mux]->sock_connected = false; | ||||
|       return true; | ||||
|     } | ||||
|     else {  // no async close | ||||
|       sendAT(GF("+USOCL="), mux); | ||||
|       return (1 == waitResponse()); | ||||
|     bool success; | ||||
|     sendAT(GF("+USOCL="), mux); | ||||
|     success = 1 == waitResponse();  // should return within 1s | ||||
|     if (success) { | ||||
|       sockets[mux]->sock_connected = false; | ||||
|     } | ||||
|     return success; | ||||
|   } | ||||
|  | ||||
|   int16_t modemSend(const void* buff, size_t len, uint8_t mux) { | ||||
| @@ -808,8 +582,7 @@ protected: | ||||
|     } | ||||
|     streamSkipUntil(','); // Skip mux | ||||
|     int sent = stream.readStringUntil('\n').toInt(); | ||||
|     waitResponse(); | ||||
|     maintain();  // look for a very quick response from the remote | ||||
|     waitResponse();  // sends back OK after the confirmation of number sent | ||||
|     return sent; | ||||
|   } | ||||
|  | ||||
| @@ -824,18 +597,16 @@ protected: | ||||
|     streamSkipUntil('\"'); | ||||
|  | ||||
|     for (size_t i=0; i<len; i++) { | ||||
|       while (!stream.available()) { TINY_GSM_YIELD(); } | ||||
|       char c = stream.read(); | ||||
|       sockets[mux]->rx.put(c); | ||||
|       TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT | ||||
|     } | ||||
|     streamSkipUntil('\"'); | ||||
|     waitResponse(); | ||||
|     DBG("### READ:", len, "from", mux); | ||||
|     maintain();  // Listen for a close or other URC | ||||
|     return len; | ||||
|   } | ||||
|  | ||||
|   size_t modemGetAvailable(uint8_t mux) { | ||||
|     // NOTE:  Querying a closed socket gives an error "operation not allowed" | ||||
|     sendAT(GF("+USORD="), mux, ",0"); | ||||
|     size_t result = 0; | ||||
|     uint8_t res = waitResponse(GF(GSM_NL "+USORD:")); | ||||
| @@ -844,17 +615,17 @@ protected: | ||||
|     if (res == 1) { | ||||
|       streamSkipUntil(','); // Skip mux | ||||
|       result = stream.readStringUntil('\n').toInt(); | ||||
|       DBG("### DATA AVAILABLE:", result, "on", mux); | ||||
|       // if (result) DBG("### DATA AVAILABLE:", result, "on", mux); | ||||
|       waitResponse(); | ||||
|     } | ||||
|     if (!result && res != 2 && res != 3) {  // Don't check modemGetConnected after an error | ||||
|       sockets[mux]->sock_connected = modemGetConnected(mux); | ||||
|     } | ||||
|     maintain();  // Listen for a close or other URC | ||||
|     return result; | ||||
|   } | ||||
|  | ||||
|   bool modemGetConnected(uint8_t mux) { | ||||
|     // NOTE:  Querying a closed socket gives an error "operation not allowed" | ||||
|     sendAT(GF("+USOCTL="), mux, ",10"); | ||||
|     uint8_t res = waitResponse(GF(GSM_NL "+USOCTL:")); | ||||
|     if (res != 1) | ||||
| @@ -876,7 +647,6 @@ protected: | ||||
|     // 9: the socket is in LAST_ACK status | ||||
|     // 10: the socket is in TIME_WAIT status | ||||
|     waitResponse(); | ||||
|     maintain();  // Listen for a close or other URC | ||||
|     return (result != 0); | ||||
|   } | ||||
|  | ||||
| @@ -886,16 +656,10 @@ public: | ||||
|    Utilities | ||||
|    */ | ||||
|  | ||||
|   template<typename... Args> | ||||
|   void sendAT(Args... cmd) { | ||||
|     streamWrite("AT", cmd..., GSM_NL); | ||||
|     stream.flush(); | ||||
|     TINY_GSM_YIELD(); | ||||
|     //DBG("### AT:", cmd...); | ||||
|   } | ||||
| TINY_GSM_MODEM_STREAM_UTILITIES() | ||||
|  | ||||
|   // TODO: Optimize this! | ||||
|   uint8_t waitResponse(uint32_t timeout, String& data, | ||||
|   uint8_t waitResponse(uint32_t timeout_ms, String& data, | ||||
|                        GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|                        GsmConstStr r3=GFP(GSM_CME_ERROR), GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||
|   { | ||||
| @@ -937,17 +701,17 @@ public: | ||||
|             sockets[mux]->sock_available = len; | ||||
|           } | ||||
|           data = ""; | ||||
|           DBG("### Got Data:", len, "on", mux); | ||||
|           DBG("### URC Data Received:", len, "on", mux); | ||||
|         } else if (data.endsWith(GF(GSM_NL "+UUSOCL:"))) { | ||||
|           int mux = stream.readStringUntil('\n').toInt(); | ||||
|           if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { | ||||
|             sockets[mux]->sock_connected = false; | ||||
|           } | ||||
|           data = ""; | ||||
|           DBG("### Closed: ", mux); | ||||
|           DBG("### URC Sock Closed: ", mux); | ||||
|         } | ||||
|       } | ||||
|     } while (millis() - startMillis < timeout); | ||||
|     } while (millis() - startMillis < timeout_ms); | ||||
| finish: | ||||
|     if (!index) { | ||||
|       data.trim(); | ||||
| @@ -960,12 +724,12 @@ finish: | ||||
|     return index; | ||||
|   } | ||||
|  | ||||
|   uint8_t waitResponse(uint32_t timeout, | ||||
|   uint8_t waitResponse(uint32_t timeout_ms, | ||||
|                        GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|                        GsmConstStr r3=GFP(GSM_CME_ERROR), GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||
|   { | ||||
|     String data; | ||||
|     return waitResponse(timeout, data, r1, r2, r3, r4, r5); | ||||
|     return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5); | ||||
|   } | ||||
|  | ||||
|   uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
| @@ -979,7 +743,6 @@ public: | ||||
|  | ||||
| protected: | ||||
|   GsmClient*    sockets[TINY_GSM_MUX_COUNT]; | ||||
|   bool          isCatM; | ||||
| }; | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -24,6 +24,20 @@ | ||||
| static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; | ||||
| static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; | ||||
|  | ||||
| // Use this to avoid too many entrances and exits from command mode. | ||||
| // The cellular Bee's often freeze up and won't respond when attempting | ||||
| // to enter command mode too many times. | ||||
| #define XBEE_COMMAND_START_DECORATOR(nAttempts, failureReturn) \ | ||||
|   bool wasInCommandMode = inCommandMode; \ | ||||
|   if (!wasInCommandMode) {  /* don't re-enter command mode if already in it */ \ | ||||
|     if (!commandMode(nAttempts)) return failureReturn;  /* Return immediately if fails */ \ | ||||
|   } | ||||
| #define XBEE_COMMAND_END_DECORATOR \ | ||||
|   if (!wasInCommandMode) {  /* only exit if we weren't in command mode */ \ | ||||
|     exitCommand(); \ | ||||
|   } | ||||
|  | ||||
|  | ||||
| enum SimStatus { | ||||
|   SIM_ERROR = 0, | ||||
|   SIM_READY = 1, | ||||
| @@ -49,7 +63,7 @@ enum XBeeType { | ||||
| }; | ||||
|  | ||||
|  | ||||
| class TinyGsmXBee : public TinyGsmModem | ||||
| class TinyGsmXBee | ||||
| { | ||||
|  | ||||
| public: | ||||
| @@ -77,31 +91,30 @@ public: | ||||
|   } | ||||
|  | ||||
| public: | ||||
|   // NOTE:  The XBee saves all paramter information in flash.  When you turn it | ||||
|   // on it immediately prepares to re-connect to whatever was last connected to. | ||||
|   // All the modemConnect() function does is tell it the paramters to put into | ||||
|   // flash.  The connection itself is not opened until you attempt to send data. | ||||
|   // NOTE:  The XBee saves all connection information (ssid/pwd or apn AND last used IP address) | ||||
|   // in flash (NVM).  When you turn it on it immediately prepares to re-connect to whatever was | ||||
|   // last set.  The TCP connection itself is not opened until you attempt to send data. | ||||
|   // Because all settings are saved to flash, it is possible (or likely) that | ||||
|   // you could send out data even if you haven't "made" any connection. | ||||
|   virtual int connect(const char *host, uint16_t port) { | ||||
|   // you could send data even if you haven't "made" any connection. | ||||
|   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||
|     // NOTE:  Not caling stop() or yeild() here | ||||
|     at->streamClear();  // Empty anything in the buffer before starting | ||||
|     if (at->commandMode())  {  // Don't try if we didn't successfully get into command mode | ||||
|       sock_connected = at->modemConnect(host, port, mux, false); | ||||
|       at->writeChanges(); | ||||
|       at->exitCommand(); | ||||
|     } | ||||
|     sock_connected = at->modemConnect(host, port, mux, false, timeout_s); | ||||
|     return sock_connected; | ||||
|   } | ||||
|   virtual int connect(const char *host, uint16_t port) { | ||||
|     return connect(host, port, 75); | ||||
|   } | ||||
|  | ||||
|   virtual int connect(IPAddress ip, uint16_t port) { | ||||
|   virtual int connect(IPAddress ip, uint16_t port, int timeout_s) { | ||||
|     // NOTE:  Not caling stop() or yeild() here | ||||
|     at->streamClear();  // Empty anything in the buffer before starting | ||||
|     if (at->commandMode())  {  // Don't try if we didn't successfully get into command mode | ||||
|       sock_connected = at->modemConnect(ip, port, mux, false); | ||||
|       at->writeChanges(); | ||||
|       at->exitCommand(); | ||||
|     } | ||||
|     sock_connected = at->modemConnect(ip, port, mux, timeout_s); | ||||
|     return sock_connected; | ||||
|   } | ||||
|   virtual int connect(IPAddress ip, uint16_t port) { | ||||
|     return connect(ip, port, 75); | ||||
|   } | ||||
|  | ||||
|   virtual void stop() { | ||||
|     at->streamClear();  // Empty anything in the buffer | ||||
| @@ -196,12 +209,6 @@ public: | ||||
|     if (available()) { | ||||
|       return true; | ||||
|     } | ||||
|     // Double check that we don't know it's closed | ||||
|     // NOTE:  modemGetConnected() is likely to return a "false" true because | ||||
|     // it will return unknown until after data is sent over the connection. | ||||
|     // If the socket is definitely closed, modemGetConnected() will set | ||||
|     // sock_connected to false; | ||||
|     at->modemGetConnected(); | ||||
|     return sock_connected; | ||||
|   } | ||||
|   virtual operator bool() { return connected(); } | ||||
| @@ -230,23 +237,17 @@ public: | ||||
|   {} | ||||
|  | ||||
| public: | ||||
|   virtual int connect(const char *host, uint16_t port) { | ||||
|   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||
|     // NOTE:  Not caling stop() or yeild() here | ||||
|     at->streamClear();  // Empty anything in the buffer before starting | ||||
|     if (at->commandMode())  {  // Don't try if we didn't successfully get into command mode | ||||
|       sock_connected = at->modemConnect(host, port, mux, true); | ||||
|       at->writeChanges(); | ||||
|       at->exitCommand(); | ||||
|     } | ||||
|     sock_connected = at->modemConnect(host, port, mux, true, timeout_s); | ||||
|     return sock_connected; | ||||
|   } | ||||
|  | ||||
|   virtual int connect(IPAddress ip, uint16_t port) { | ||||
|   virtual int connect(IPAddress ip, uint16_t port, int timeout_s) { | ||||
|     // NOTE:  Not caling stop() or yeild() here | ||||
|     at->streamClear();  // Empty anything in the buffer before starting | ||||
|     if (at->commandMode())  {  // Don't try if we didn't successfully get into command mode | ||||
|       sock_connected = at->modemConnect(ip, port, mux, false); | ||||
|       at->writeChanges(); | ||||
|       at->exitCommand(); | ||||
|     } | ||||
|     sock_connected = at->modemConnect(ip, port, mux, timeout_s); | ||||
|     return sock_connected; | ||||
|   } | ||||
| }; | ||||
| @@ -255,24 +256,26 @@ public: | ||||
| public: | ||||
|  | ||||
|   TinyGsmXBee(Stream& stream) | ||||
|     : TinyGsmModem(stream), stream(stream) | ||||
|     : stream(stream) | ||||
|   { | ||||
|       beeType = XBEE_UNKNOWN;  // Start not knowing what kind of bee it is | ||||
|       guardTime = TINY_GSM_XBEE_GUARD_TIME;  // Start with the default guard time of 1 second | ||||
|       resetPin = -1; | ||||
|       savedIP = IPAddress(0,0,0,0); | ||||
|       savedHost = ""; | ||||
|       inCommandMode = false; | ||||
|       memset(sockets, 0, sizeof(sockets)); | ||||
|   } | ||||
|  | ||||
|   TinyGsmXBee(Stream& stream, int8_t resetPin) | ||||
|     : TinyGsmModem(stream), stream(stream) | ||||
|     : stream(stream) | ||||
|   { | ||||
|       beeType = XBEE_UNKNOWN;  // Start not knowing what kind of bee it is | ||||
|       guardTime = TINY_GSM_XBEE_GUARD_TIME;  // Start with the default guard time of 1 second | ||||
|       this->resetPin = resetPin; | ||||
|       savedIP = IPAddress(0,0,0,0); | ||||
|       savedHost = ""; | ||||
|       inCommandMode = false; | ||||
|       memset(sockets, 0, sizeof(sockets)); | ||||
|   } | ||||
|  | ||||
| @@ -280,6 +283,10 @@ public: | ||||
|    * Basic functions | ||||
|    */ | ||||
|  | ||||
|   bool begin(const char* pin = NULL) { | ||||
|     return init(pin); | ||||
|   } | ||||
|  | ||||
|   bool init(const char* pin = NULL) { | ||||
|     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); | ||||
|  | ||||
| @@ -288,20 +295,26 @@ public: | ||||
|       digitalWrite(resetPin, HIGH); | ||||
|     } | ||||
|  | ||||
|     if (!commandMode(10)) return false;  // Try up to 10 times for the init | ||||
|     XBEE_COMMAND_START_DECORATOR(10, false) | ||||
|  | ||||
|     sendAT(GF("AP0"));  // Put in transparent mode | ||||
|     bool ret_val = waitResponse() == 1; | ||||
|     ret_val &= writeChanges(); | ||||
|  | ||||
|     sendAT(GF("GT64")); // shorten the guard time to 100ms | ||||
|     ret_val &= waitResponse(); | ||||
|     ret_val &= writeChanges(); | ||||
|     ret_val &= waitResponse() == 1; | ||||
|     if (ret_val) guardTime = 110; | ||||
|  | ||||
|    // Make sure the command mode drop-out time is long enough that we won't fall | ||||
|    // out of command mode without intentionally leaving it.  This is the default | ||||
|    // drop out time of 0x64 x 100ms (10 seconds) | ||||
|     sendAT(GF("CT64")); | ||||
|     ret_val &= waitResponse() == 1; | ||||
|     ret_val &= writeChanges(); | ||||
|  | ||||
|     getSeries();  // Get the "Hardware Series"; | ||||
|  | ||||
|     exitCommand(); | ||||
|     XBEE_COMMAND_END_DECORATOR | ||||
|  | ||||
|     return ret_val; | ||||
|   } | ||||
|  | ||||
| @@ -310,7 +323,7 @@ public: | ||||
|   } | ||||
|  | ||||
|   void setBaud(unsigned long baud) { | ||||
|     if (!commandMode()) return; | ||||
|     XBEE_COMMAND_START_DECORATOR(5, ) | ||||
|     switch(baud) | ||||
|     { | ||||
|       case 2400: sendAT(GF("BD1")); break; | ||||
| @@ -331,40 +344,48 @@ public: | ||||
|     } | ||||
|     waitResponse(); | ||||
|     writeChanges(); | ||||
|     exitCommand(); | ||||
|     XBEE_COMMAND_END_DECORATOR | ||||
|   } | ||||
|  | ||||
|   bool testAT(unsigned long timeout = 10000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { | ||||
|       if (commandMode()) | ||||
|       { | ||||
|   bool testAT(unsigned long timeout_ms = 10000L) { | ||||
|     unsigned long start = millis(); | ||||
|     bool success = false; | ||||
|     while (!success && millis() - start < timeout_ms) { | ||||
|       if (!inCommandMode) { | ||||
|         success = commandMode(); | ||||
|         if (success) exitCommand(); | ||||
|       } | ||||
|       else { | ||||
|           sendAT(); | ||||
|           if (waitResponse(200) == 1) { | ||||
|               exitCommand(); | ||||
|               return true; | ||||
|               success =  true; | ||||
|           } | ||||
|           // if we didn't respond to the AT, assume we're not in command mode | ||||
|           else inCommandMode = false; | ||||
|       } | ||||
|       delay(100); | ||||
|       delay(250); | ||||
|     } | ||||
|     return false; | ||||
|     return success; | ||||
|   } | ||||
|  | ||||
|   void maintain() { | ||||
|     // this only happens OUTSIDE command mode, so if we're getting characters | ||||
|     // they should be data received from the TCP connection | ||||
|     // TINY_GSM_YIELD(); | ||||
|     // while (stream.available()) { | ||||
|     //   char c = stream.read(); | ||||
|     //   if (c > 0) sockets[0]->rx.put(c); | ||||
|     // if (!inCommandMode) { | ||||
|     //   while (stream.available()) { | ||||
|     //     char c = stream.read(); | ||||
|     //     if (c > 0) sockets[0]->rx.put(c); | ||||
|     //   } | ||||
|     // } | ||||
|   } | ||||
|  | ||||
|   bool factoryDefault() { | ||||
|     if (!commandMode()) return false;  // Return immediately | ||||
|     XBEE_COMMAND_START_DECORATOR(5, false) | ||||
|     sendAT(GF("RE")); | ||||
|     bool ret_val = waitResponse() == 1; | ||||
|     ret_val &= writeChanges(); | ||||
|     exitCommand(); | ||||
|     XBEE_COMMAND_END_DECORATOR | ||||
|     // Make sure the guard time for the modem object is set back to default | ||||
|     // otherwise communication would fail after the reset | ||||
|     guardTime = 1010; | ||||
| @@ -372,14 +393,7 @@ public: | ||||
|   } | ||||
|  | ||||
|   String getModemInfo() { | ||||
|     String modemInf = ""; | ||||
|     if (!commandMode()) return modemInf;  // Try up to 10 times for the init | ||||
|  | ||||
|     sendAT(GF("HS"));  // Get the "Hardware Series" | ||||
|     modemInf += readResponseString(); | ||||
|  | ||||
|     exitCommand(); | ||||
|     return modemInf; | ||||
|     return sendATGetString(GF("HS")); | ||||
|   } | ||||
|  | ||||
|   bool hasSSL() { | ||||
| @@ -428,7 +442,9 @@ public: | ||||
|   } | ||||
|  | ||||
|   bool restart() { | ||||
|  | ||||
|     if (!commandMode()) return false;  // Return immediately | ||||
|  | ||||
|     if (beeType == XBEE_UNKNOWN) getSeries();  // how we restart depends on this | ||||
|  | ||||
|     if (beeType != XBEE_S6B_WIFI) { | ||||
| @@ -440,9 +456,10 @@ public: | ||||
|  | ||||
|     sendAT(GF("FR")); | ||||
|     if (waitResponse() != 1) return exitAndFail(); | ||||
|     else inCommandMode = false;  // Reset effectively exits command mode | ||||
|  | ||||
|     if (beeType == XBEE_S6B_WIFI) delay(2000);  // Wifi module actually resets about 2 seconds later | ||||
|     else delay(100);  // cellular modules wait 100ms before reset happes | ||||
|     else delay(100);  // cellular modules wait 100ms before reset happens | ||||
|  | ||||
|     // Wait until reboot complete and responds to command mode call again | ||||
|     for (unsigned long start = millis(); millis() - start < 60000L; ) { | ||||
| @@ -462,7 +479,7 @@ public: | ||||
|   } | ||||
|  | ||||
|   void setupPinSleep(bool maintainAssociation = false) { | ||||
|     if (!commandMode()) return;  // Return immediately | ||||
|     XBEE_COMMAND_START_DECORATOR(5, ) | ||||
|  | ||||
|     if (beeType == XBEE_UNKNOWN) getSeries();  // Command depends on series | ||||
|  | ||||
| @@ -481,7 +498,7 @@ public: | ||||
|     } | ||||
|  | ||||
|     writeChanges(); | ||||
|     exitCommand(); | ||||
|     XBEE_COMMAND_END_DECORATOR | ||||
|   } | ||||
|  | ||||
|   bool poweroff() {  // Not supported | ||||
| @@ -501,32 +518,27 @@ public: | ||||
|   } | ||||
|  | ||||
|   String getSimCCID() { | ||||
|     if (!commandMode()) return "";  // Return immediately | ||||
|     sendAT(GF("S#")); | ||||
|     String res = readResponseString(); | ||||
|     exitCommand(); | ||||
|     return res; | ||||
|     return sendATGetString(GF("S#")); | ||||
|   } | ||||
|  | ||||
|   String getIMEI() { | ||||
|     if (!commandMode()) return "";  // Return immediately | ||||
|     sendAT(GF("IM")); | ||||
|     String res = readResponseString(); | ||||
|     exitCommand(); | ||||
|     return res; | ||||
|     return sendATGetString(GF("IM")); | ||||
|   } | ||||
|  | ||||
|   SimStatus getSimStatus(unsigned long timeout = 10000L) { | ||||
|   SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { | ||||
|     return SIM_READY;  // unsupported | ||||
|   } | ||||
|  | ||||
|   RegStatus getRegistrationStatus() { | ||||
|     if (!commandMode()) return REG_UNKNOWN;  // Return immediately | ||||
|  | ||||
|     XBEE_COMMAND_START_DECORATOR(5, REG_UNKNOWN) | ||||
|  | ||||
|     if (!inCommandMode) return REG_UNKNOWN;  // Return immediately | ||||
|  | ||||
|     if (beeType == XBEE_UNKNOWN) getSeries();  // Need to know the bee type to interpret response | ||||
|  | ||||
|     sendAT(GF("AI")); | ||||
|     int16_t intRes = readResponseInt(); | ||||
|     int16_t intRes = readResponseInt(10000L); | ||||
|     RegStatus stat = REG_UNKNOWN; | ||||
|  | ||||
|     switch (beeType){ | ||||
| @@ -598,16 +610,12 @@ public: | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     exitCommand(); | ||||
|     XBEE_COMMAND_END_DECORATOR | ||||
|     return stat; | ||||
|   } | ||||
|  | ||||
|   String getOperator() { | ||||
|     if (!commandMode()) return "";  // Return immediately | ||||
|     sendAT(GF("MN")); | ||||
|     String res = readResponseString(); | ||||
|     exitCommand(); | ||||
|     return res; | ||||
|     return sendATGetString(GF("MN")); | ||||
|   } | ||||
|  | ||||
|  /* | ||||
| @@ -615,12 +623,17 @@ public: | ||||
|   */ | ||||
|  | ||||
|   int16_t getSignalQuality() { | ||||
|     if (!commandMode()) return 0;  // Return immediately | ||||
|  | ||||
|     XBEE_COMMAND_START_DECORATOR(5, 0); | ||||
|  | ||||
|     if (beeType == XBEE_UNKNOWN) getSeries();  // Need to know what type of bee so we know how to ask | ||||
|  | ||||
|     if (beeType == XBEE_S6B_WIFI) sendAT(GF("LM"));  // ask for the "link margin" - the dB above sensitivity | ||||
|     else sendAT(GF("DB"));  // ask for the cell strength in dBm | ||||
|     int16_t intRes = readResponseInt(); | ||||
|     exitCommand(); | ||||
|  | ||||
|     XBEE_COMMAND_END_DECORATOR | ||||
|  | ||||
|     if (beeType == XBEE3_LTEM_ATT && intRes == 105) intRes = 0;  // tends to reply with "69" when signal is unknown | ||||
|     if (beeType == XBEE_S6B_WIFI) return -93 + intRes;  // the maximum sensitivity is -93dBm | ||||
|     else return -1*intRes; // need to convert to negative number | ||||
| @@ -631,52 +644,60 @@ public: | ||||
|     return (s == REG_OK); | ||||
|   } | ||||
|  | ||||
|   bool waitForNetwork(unsigned long timeout = 60000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { | ||||
|   bool waitForNetwork(unsigned long timeout_ms = 60000L) { | ||||
|     bool retVal = false; | ||||
|     XBEE_COMMAND_START_DECORATOR(5, false) | ||||
|     for (unsigned long start = millis(); millis() - start < timeout_ms; ) { | ||||
|       if (isNetworkConnected()) { | ||||
|         return true; | ||||
|         retVal = true; | ||||
|         break; | ||||
|       } | ||||
|       delay(250);  // per Neil H. - more stable with delay | ||||
|     } | ||||
|     return false; | ||||
|     XBEE_COMMAND_END_DECORATOR | ||||
|     return retVal; | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    * WiFi functions | ||||
|    */ | ||||
|  | ||||
|   bool networkConnect(const char* ssid, const char* pwd) { | ||||
|  | ||||
|     if (!commandMode()) return false;  // return immediately | ||||
|     //nh For no pwd don't set setscurity or pwd | ||||
|     if (NULL == ssid ) return exitAndFail(); | ||||
|     bool retVal = true; | ||||
|     XBEE_COMMAND_START_DECORATOR(5, false) | ||||
|  | ||||
|     if (NULL != pwd) | ||||
|     //nh For no pwd don't set setscurity or pwd | ||||
|     if (ssid == NULL) retVal = false;; | ||||
|  | ||||
|     if (pwd != NULL) | ||||
|     { | ||||
|       sendAT(GF("EE"), 2);  // Set security to WPA2 | ||||
|       if (waitResponse() != 1) return exitAndFail(); | ||||
|       if (waitResponse() != 1) retVal = false; | ||||
|       sendAT(GF("PK"), pwd); | ||||
|     } else { | ||||
|       sendAT(GF("EE"), 0);  // Set No security | ||||
|     } | ||||
|     if (waitResponse() != 1) return exitAndFail(); | ||||
|     if (waitResponse() != 1) retVal = false; | ||||
|  | ||||
|     sendAT(GF("ID"), ssid); | ||||
|     if (waitResponse() != 1) return exitAndFail(); | ||||
|     if (waitResponse() != 1) retVal = false; | ||||
|  | ||||
|     if (!writeChanges()) return exitAndFail(); | ||||
|     exitCommand(); | ||||
|     if (!writeChanges()) retVal = false; | ||||
|  | ||||
|     return true; | ||||
|     XBEE_COMMAND_END_DECORATOR | ||||
|  | ||||
|     return retVal; | ||||
|   } | ||||
|  | ||||
|   bool networkDisconnect() { | ||||
|     if (!commandMode()) return false;  // return immediately | ||||
|     XBEE_COMMAND_START_DECORATOR(5, false) | ||||
|     sendAT(GF("NR0"));  // Do a network reset in order to disconnect | ||||
|     // NOTE:  On wifi modules, using a network reset will not | ||||
|     // WARNING:  On wifi modules, using a network reset will not | ||||
|     // allow the same ssid to re-join without rebooting the module. | ||||
|     int8_t res = (1 == waitResponse(5000)); | ||||
|     writeChanges(); | ||||
|     exitCommand(); | ||||
|     XBEE_COMMAND_END_DECORATOR | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
| @@ -685,37 +706,42 @@ public: | ||||
|    */ | ||||
|  | ||||
|   String getLocalIP() { | ||||
|     if (!commandMode()) return "";  // Return immediately | ||||
|     XBEE_COMMAND_START_DECORATOR(5, "") | ||||
|     sendAT(GF("MY")); | ||||
|     String IPaddr; IPaddr.reserve(16); | ||||
|     // wait for the response - this response can be very slow | ||||
|     IPaddr = readResponseString(30000); | ||||
|     exitCommand(); | ||||
|     XBEE_COMMAND_END_DECORATOR | ||||
|     IPaddr.trim(); | ||||
|     return IPaddr; | ||||
|   } | ||||
|  | ||||
|   IPAddress localIP() { | ||||
|     return TinyGsmIpFromString(getLocalIP()); | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    * GPRS functions | ||||
|    */ | ||||
|  | ||||
|   bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { | ||||
|     if (!commandMode()) return false;  // Return immediately | ||||
|     XBEE_COMMAND_START_DECORATOR(5, false) | ||||
|     sendAT(GF("AN"), apn);  // Set the APN | ||||
|     waitResponse(); | ||||
|     bool success = waitResponse() == 1; | ||||
|     writeChanges(); | ||||
|     exitCommand(); | ||||
|     return true; | ||||
|     XBEE_COMMAND_END_DECORATOR | ||||
|     return success; | ||||
|   } | ||||
|  | ||||
|   bool gprsDisconnect() { | ||||
|     if (!commandMode()) return false;  // return immediately | ||||
|     XBEE_COMMAND_START_DECORATOR(5, false) | ||||
|     sendAT(GF("AM1"));  // Cheating and disconnecting by turning on airplane mode | ||||
|     int8_t res = (1 == waitResponse(5000)); | ||||
|     writeChanges(); | ||||
|     sendAT(GF("AM0"));  // Airplane mode off | ||||
|     waitResponse(5000); | ||||
|     writeChanges(); | ||||
|     exitCommand(); | ||||
|     XBEE_COMMAND_END_DECORATOR | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
| @@ -738,11 +764,14 @@ public: | ||||
|     if (waitResponse() !=1) return exitAndFail(); | ||||
|     sendAT(GF("TDD"));  // Set the text delimiter to the standard 0x0D (carriage return) | ||||
|     if (waitResponse() !=1) return exitAndFail(); | ||||
|     if (!writeChanges()) return exitAndFail(); | ||||
|  | ||||
|     if (!writeChanges()) return exitAndFail(); | ||||
|     // Get out of command mode to actually send the text | ||||
|     exitCommand(); | ||||
|  | ||||
|     streamWrite(text); | ||||
|     stream.write((char)0x0D);  // close off with the carriage return | ||||
|  | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
| @@ -753,12 +782,25 @@ public: | ||||
|   String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|  | ||||
|   /* | ||||
|    * Battery functions | ||||
|    * Battery & temperature functions | ||||
|    */ | ||||
|  | ||||
|   // Use: float vBatt = modem.getBattVoltage() / 1000.0; | ||||
|   uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|  | ||||
|   int8_t getBattPercent() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|   uint8_t getBattChargeState() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|   bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) TINY_GSM_ATTR_NOT_AVAILABLE; | ||||
|  | ||||
|   float getTemperature() { | ||||
|     String res = sendATGetString(GF("TP")); | ||||
|     if (res == "") { | ||||
|       return (float)-9999; | ||||
|     } | ||||
|     char buf[5] = {0,}; | ||||
|     res.toCharArray(buf, 5); | ||||
|     int8_t intRes = (int8_t)strtol(buf, 0, 16); // degrees Celsius displayed in 8-bit two's complement format. | ||||
|     return (float)intRes; | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    * Client related functions | ||||
| @@ -766,51 +808,66 @@ public: | ||||
|  | ||||
| protected: | ||||
|  | ||||
|   IPAddress getHostIP(const char* host) { | ||||
|   IPAddress getHostIP(const char* host, int timeout_s = 45) { | ||||
|     String strIP; strIP.reserve(16); | ||||
|     unsigned long startMillis = millis(); | ||||
|     uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; | ||||
|     bool gotIP = false; | ||||
|     XBEE_COMMAND_START_DECORATOR(5, IPAddress(0,0,0,0)) | ||||
|     // XBee's require a numeric IP address for connection, but do provide the | ||||
|     // functionality to look up the IP address from a fully qualified domain name | ||||
|     while (millis() - startMillis < 45000L)  // the lookup can take a while | ||||
|     while ((millis() - startMillis) < timeout_ms)  // the lookup can take a while | ||||
|     { | ||||
|       sendAT(GF("LA"), host); | ||||
|       while (stream.available() < 4 && (millis() - startMillis < 45000L)) {};  // wait for any response | ||||
|       while (stream.available() < 4 && (millis() - startMillis < timeout_ms)) {TINY_GSM_YIELD()}; | ||||
|       strIP = stream.readStringUntil('\r');  // read result | ||||
|       strIP.trim(); | ||||
|       if (!strIP.endsWith(GF("ERROR"))) { | ||||
|       if (strIP != "" && strIP != GF("ERROR")) { | ||||
|         gotIP = true; | ||||
|         break; | ||||
|       } | ||||
|       delay(2500);  // wait a bit before trying again | ||||
|     } | ||||
|     if (gotIP) {  // No reason to continue if we don't know the IP address | ||||
|  | ||||
|     XBEE_COMMAND_END_DECORATOR | ||||
|  | ||||
|     if (gotIP) { | ||||
|       return TinyGsmIpFromString(strIP); | ||||
|     } | ||||
|     else return IPAddress(0,0,0,0); | ||||
|   } | ||||
|  | ||||
|   bool modemConnect(const char* host, uint16_t port, uint8_t mux = 0, bool ssl = false) { | ||||
|     // If requested host is the same as the previous one and we already | ||||
|     // have a valid IP address, we don't have to do anything. | ||||
|     if (this->savedHost == String(host) && savedIP != IPAddress(0,0,0,0)) { | ||||
|       return true; | ||||
|     } | ||||
|   bool modemConnect(const char* host, uint16_t port, uint8_t mux = 0, | ||||
|                     bool ssl = false, int timeout_s = 75) | ||||
|   { | ||||
|     unsigned long startMillis = millis(); | ||||
|     uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; | ||||
|     bool retVal = false; | ||||
|      XBEE_COMMAND_START_DECORATOR(5, false) | ||||
|  | ||||
|     // Otherwise, set the new host and mark the IP as invalid | ||||
|     this->savedHost = String(host); | ||||
|     savedIP = getHostIP(host);  // This will return 0.0.0.0 if lookup fails | ||||
|     // If it's a new host or we dont' have a good IP, we need to do a DNS | ||||
|     // search for the IP to connect to | ||||
|     if (this->savedHost != String(host) || savedIP == IPAddress(0,0,0,0)) { | ||||
|       this->savedHost = String(host); | ||||
|       savedIP = getHostIP(host, timeout_s);  // This will return 0.0.0.0 if lookup fails | ||||
|     } | ||||
|  | ||||
|     // If we now have a valid IP address, use it to connect | ||||
|     if (savedIP != IPAddress(0,0,0,0)) {  // Only re-set connection information if we have an IP address | ||||
|       return modemConnect(savedIP, port, mux, ssl); | ||||
|       retVal = modemConnect(savedIP, port, mux, ssl, timeout_ms - (millis() - startMillis)); | ||||
|     } | ||||
|     else return false; | ||||
|  | ||||
|     XBEE_COMMAND_END_DECORATOR | ||||
|  | ||||
|     return retVal; | ||||
|   } | ||||
|  | ||||
|   bool modemConnect(IPAddress ip, uint16_t port, uint8_t mux = 0, bool ssl = false) { | ||||
|   bool modemConnect(IPAddress ip, uint16_t port, uint8_t mux = 0, bool ssl = false, int timeout_s = 75) { | ||||
|  | ||||
|     savedIP = ip;  // Set the newly requested IP address | ||||
|     bool success = true; | ||||
|     uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; | ||||
|     XBEE_COMMAND_START_DECORATOR(5, false) | ||||
|     String host; host.reserve(16); | ||||
|     host += ip[0]; | ||||
|     host += "."; | ||||
| @@ -819,6 +876,7 @@ protected: | ||||
|     host += ip[2]; | ||||
|     host += "."; | ||||
|     host += ip[3]; | ||||
|  | ||||
|     if (ssl) { | ||||
|       sendAT(GF("IP"), 4);  // Put in SSL over TCP communication mode | ||||
|       success &= (1 == waitResponse()); | ||||
| @@ -826,10 +884,21 @@ protected: | ||||
|       sendAT(GF("IP"), 1);  // Put in TCP mode | ||||
|       success &= (1 == waitResponse()); | ||||
|     } | ||||
|  | ||||
|     sendAT(GF("DL"), host);  // Set the "Destination Address Low" | ||||
|     success &= (1 == waitResponse()); | ||||
|     sendAT(GF("DE"), String(port, HEX));  // Set the destination port | ||||
|     success &= (1 == waitResponse()); | ||||
|  | ||||
|     for (unsigned long start = millis(); millis() - start < timeout_ms; ) { | ||||
|       if (modemGetConnected()) { | ||||
|         sockets[mux]->sock_connected = true; | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     XBEE_COMMAND_END_DECORATOR | ||||
|  | ||||
|     return success; | ||||
|   } | ||||
|  | ||||
| @@ -844,36 +913,37 @@ protected: | ||||
|   // really be open, but no data has yet been sent.  We return this unknown value | ||||
|   // as true so there's a possibility it's wrong. | ||||
|   bool modemGetConnected() { | ||||
|  | ||||
|     if (!commandMode()) return false;  // Return immediately | ||||
|  | ||||
|     // If the IP address is 0, it's not valid so we can't be connected | ||||
|     if (savedIP == IPAddress(0,0,0,0)) return false; | ||||
|  | ||||
|      XBEE_COMMAND_START_DECORATOR(5, false) | ||||
|  | ||||
|     // Verify that we're connected to the *right* IP address | ||||
|     // We might be connected - but to the wrong thing | ||||
|     // NOTE:  In transparent mode, there is only one connection possible - no multiplex | ||||
|     String strIP; strIP.reserve(16); | ||||
|     sendAT(GF("DL")); | ||||
|     strIP = stream.readStringUntil('\r');  // read result | ||||
|     if (TinyGsmIpFromString(strIP) != savedIP) return exitAndFail(); | ||||
|     // String strIP; strIP.reserve(16); | ||||
|     // sendAT(GF("DL")); | ||||
|     // strIP = stream.readStringUntil('\r');  // read result | ||||
|     // if (TinyGsmIpFromString(strIP) != savedIP) return exitAndFail(); | ||||
|  | ||||
|     if (beeType == XBEE_UNKNOWN) getSeries();  // Need to know the bee type to interpret response | ||||
|  | ||||
|     switch (beeType){  // The wifi be can only say if it's connected to the netowrk | ||||
|       case XBEE_S6B_WIFI: { | ||||
|         RegStatus s = getRegistrationStatus(); | ||||
|         XBEE_COMMAND_END_DECORATOR | ||||
|         if (s != REG_OK) { | ||||
|           sockets[0]->sock_connected = false; | ||||
|           sockets[0]->sock_connected = false;  // no multiplex | ||||
|         } | ||||
|         return (s == REG_OK);  // if it's connected, we hope the sockets are too | ||||
|       } | ||||
|       default: {  // Cellular XBee's | ||||
|         sendAT(GF("CI")); | ||||
|         int16_t intRes = readResponseInt(); | ||||
|         exitCommand(); | ||||
|         XBEE_COMMAND_END_DECORATOR | ||||
|         switch(intRes) { | ||||
|           case 0x00:  // 0x00 = The socket is definitely open | ||||
|           case 0x28:  // 0x28 = "Unknown." | ||||
|           case 0xFF:  // 0xFF = No known status - this is always returned prior to sending data | ||||
|             return true; | ||||
|           case 0x02:  // 0x02 = Invalid parameters (bad IP/host) | ||||
| @@ -901,19 +971,13 @@ public: | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   template<typename... Args> | ||||
|   void sendAT(Args... cmd) { | ||||
|     streamWrite("AT", cmd..., GSM_NL); | ||||
|     stream.flush(); | ||||
|     TINY_GSM_YIELD(); | ||||
|     //DBG("### AT:", cmd...); | ||||
|   } | ||||
| TINY_GSM_MODEM_STREAM_UTILITIES() | ||||
|  | ||||
|   // TODO: Optimize this! | ||||
|   // NOTE:  This function is used while INSIDE command mode, so we're only | ||||
|   // waiting for requested responses.  The XBee has no unsoliliced responses | ||||
|   // (URC's) when in command mode. | ||||
|   uint8_t waitResponse(uint32_t timeout, String& data, | ||||
|   uint8_t waitResponse(uint32_t timeout_ms, String& data, | ||||
|                        GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||
|   { | ||||
| @@ -949,7 +1013,7 @@ public: | ||||
|           goto finish; | ||||
|         } | ||||
|       } | ||||
|     } while (millis() - startMillis < timeout); | ||||
|     } while (millis() - startMillis < timeout_ms); | ||||
| finish: | ||||
|     if (!index) { | ||||
|       data.trim(); | ||||
| @@ -971,12 +1035,12 @@ finish: | ||||
|     return index; | ||||
|   } | ||||
|  | ||||
|   uint8_t waitResponse(uint32_t timeout, | ||||
|   uint8_t waitResponse(uint32_t timeout_ms, | ||||
|                        GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||
|   { | ||||
|     String data; | ||||
|     return waitResponse(timeout, data, r1, r2, r3, r4, r5); | ||||
|     return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5); | ||||
|   } | ||||
|  | ||||
|   uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||
| @@ -985,28 +1049,39 @@ finish: | ||||
|     return waitResponse(1000, r1, r2, r3, r4, r5); | ||||
|   } | ||||
|  | ||||
|   bool commandMode(uint8_t retries = 3) { | ||||
|   bool commandMode(uint8_t retries = 5) { | ||||
|  | ||||
|     // If we're already in command mode, move on | ||||
|     if (inCommandMode && (millis() - lastCommandModeMillis) < 10000L) return true; | ||||
|  | ||||
|     uint8_t triesMade = 0; | ||||
|     uint8_t triesUntilReset = 2;  // only reset after 2 failures | ||||
|     uint8_t triesUntilReset = 4;  // only reset after 4 failures | ||||
|     bool success = false; | ||||
|     streamClear();  // Empty everything in the buffer before starting | ||||
|  | ||||
|     while (!success and triesMade < retries) { | ||||
|       // Cannot send anything for 1 "guard time" before entering command mode | ||||
|       // Default guard time is 1s, but the init fxn decreases it to 100 ms | ||||
|       delay(guardTime + 50); | ||||
|       delay(guardTime + 10); | ||||
|       streamWrite(GF("+++"));  // enter command mode | ||||
|       int res = waitResponse(guardTime*2); | ||||
|       success = (1 == res); | ||||
|       if (0 == res) { | ||||
|         triesUntilReset--; | ||||
|         if (triesUntilReset == 0) { | ||||
|           triesUntilReset = 2; | ||||
|           triesUntilReset = 4; | ||||
|           pinReset();  // if it's unresponsive, reset | ||||
|           delay(250);  // a short delay to allow it to come back up TODO-optimize this | ||||
|           delay(250);  // a short delay to allow it to come back up | ||||
|           // TODO-optimize this | ||||
|         } | ||||
|       } | ||||
|       triesMade ++; | ||||
|     } | ||||
|  | ||||
|     if (success) { | ||||
|       inCommandMode = true; | ||||
|       lastCommandModeMillis = millis(); | ||||
|     } | ||||
|     return success; | ||||
|   } | ||||
|  | ||||
| @@ -1019,8 +1094,11 @@ finish: | ||||
|   } | ||||
|  | ||||
|   void exitCommand(void) { | ||||
|     // NOTE:  Here we explicitely try to exit command mode | ||||
|     // even if the internal flag inCommandMode was already false | ||||
|     sendAT(GF("CN"));  // Exit command mode | ||||
|     waitResponse(); | ||||
|     inCommandMode = false; | ||||
|   } | ||||
|  | ||||
|   bool exitAndFail(void) { | ||||
| @@ -1035,23 +1113,37 @@ finish: | ||||
|     DBG(GF("### Modem: "), getModemName()); | ||||
|   } | ||||
|  | ||||
|   String readResponseString(uint32_t timeout = 1000) { | ||||
|   String readResponseString(uint32_t timeout_ms = 1000) { | ||||
|     TINY_GSM_YIELD(); | ||||
|     unsigned long startMillis = millis(); | ||||
|     while (!stream.available() && millis() - startMillis < timeout) {}; | ||||
|     while (!stream.available() && millis() - startMillis < timeout_ms) {}; | ||||
|     String res = stream.readStringUntil('\r');  // lines end with carriage returns | ||||
|     res.trim(); | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   int16_t readResponseInt(uint32_t timeout = 1000) { | ||||
|     String res = readResponseString(timeout);  // it just works better reading a string first | ||||
|   int16_t readResponseInt(uint32_t timeout_ms = 1000) { | ||||
|     String res = readResponseString(timeout_ms);  // it just works better reading a string first | ||||
|     if (res == "") res = "FF"; | ||||
|     char buf[5] = {0,}; | ||||
|     res.toCharArray(buf, 5); | ||||
|     int16_t intRes = strtol(buf, 0, 16); | ||||
|     return intRes; | ||||
|   } | ||||
|  | ||||
|   String sendATGetString(GsmConstStr cmd) { | ||||
|     XBEE_COMMAND_START_DECORATOR(5, "") | ||||
|     sendAT(cmd); | ||||
|     String res = readResponseString(); | ||||
|     XBEE_COMMAND_END_DECORATOR | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   bool gotIPforSavedHost() { | ||||
|     if (savedHost != "" && savedIP != IPAddress(0,0,0,0)) return true; | ||||
|     else return false; | ||||
|   } | ||||
|  | ||||
| public: | ||||
|   Stream&       stream; | ||||
|  | ||||
| @@ -1061,6 +1153,8 @@ protected: | ||||
|   XBeeType      beeType; | ||||
|   IPAddress     savedIP; | ||||
|   String        savedHost; | ||||
|   bool          inCommandMode; | ||||
|   uint32_t      lastCommandModeMillis; | ||||
|   GsmClient*    sockets[TINY_GSM_MUX_COUNT]; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
| #define TinyGsmCommon_h | ||||
|  | ||||
| // The current library version number | ||||
| #define TINYGSM_VERSION "0.6.2" | ||||
| #define TINYGSM_VERSION "0.7.4" | ||||
|  | ||||
| #if defined(SPARK) || defined(PARTICLE) | ||||
|   #include "Particle.h" | ||||
| @@ -34,10 +34,6 @@ | ||||
|   #define TINY_GSM_YIELD() { delay(0); } | ||||
| #endif | ||||
|  | ||||
| #if !defined(TINY_GSM_RX_BUFFER) | ||||
|   #define TINY_GSM_RX_BUFFER 256 | ||||
| #endif | ||||
|  | ||||
| #define TINY_GSM_ATTR_NOT_AVAILABLE __attribute__((error("Not available on this modem type"))) | ||||
| #define TINY_GSM_ATTR_NOT_IMPLEMENTED __attribute__((error("Not implemented"))) | ||||
|  | ||||
| @@ -203,136 +199,425 @@ String TinyGsmDecodeHex16bit(String &instr) { | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| class TinyGsmModem | ||||
| { | ||||
|  | ||||
| public: | ||||
|  | ||||
|   TinyGsmModem(Stream& stream) | ||||
|     : stream(stream) | ||||
|   {} | ||||
|  | ||||
|   /* | ||||
|    * Basic functions | ||||
|    */ | ||||
|  | ||||
|   // Prepare the modem for further functionality | ||||
|   virtual bool init(const char* pin = NULL) = 0; | ||||
|   // Begin is redundant with init | ||||
|   virtual bool begin(const char* pin = NULL) { | ||||
|     return init(pin); | ||||
|   } | ||||
|   // Returns a string with the chip name | ||||
|   virtual String getModemName() = 0; | ||||
|   // Sets the serial communication baud rate | ||||
|   virtual void setBaud(unsigned long baud) = 0; | ||||
|   // Checks that the modem is responding to standard AT commands | ||||
|   virtual bool testAT(unsigned long timeout = 10000L) = 0; | ||||
|   // Holds open communication with the modem waiting for data to come in | ||||
|   virtual void maintain() = 0; | ||||
|   // Resets all modem chip settings to factor defaults | ||||
|   virtual bool factoryDefault() = 0; | ||||
|   // Returns the response to a get info request.  The format varies by modem. | ||||
|   virtual String getModemInfo() = 0; | ||||
|   // Answers whether types of communication are available on this modem | ||||
|   virtual bool hasSSL() = 0; | ||||
|   virtual bool hasWifi() = 0; | ||||
|   virtual bool hasGPRS() = 0; | ||||
|  | ||||
|   /* | ||||
|    * Power functions | ||||
|    */ | ||||
|  | ||||
|   virtual bool restart() = 0; | ||||
|   virtual bool poweroff() = 0; | ||||
|  | ||||
|   /* | ||||
|    * SIM card functions - only apply to cellular modems | ||||
|    */ | ||||
|  | ||||
|   virtual bool simUnlock(const char *pin) { return false; } | ||||
|   virtual String getSimCCID() { return ""; } | ||||
|   virtual String getIMEI() { return ""; } | ||||
|   virtual String getOperator() { return ""; } | ||||
|  | ||||
|  /* | ||||
|   * Generic network functions | ||||
|   */ | ||||
|  | ||||
|   virtual int16_t getSignalQuality() = 0; | ||||
|   // NOTE:  this returns whether the modem is registered on the cellular or WiFi | ||||
|   // network NOT whether GPRS or other internet connections are available | ||||
|   virtual bool isNetworkConnected() = 0; | ||||
|   virtual bool waitForNetwork(unsigned long timeout = 60000L) { | ||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { | ||||
|       if (isNetworkConnected()) { | ||||
|         return true; | ||||
|       } | ||||
|       delay(250); | ||||
|     } | ||||
|     return false; | ||||
| // Connect to a IP address given as an IPAddress object by | ||||
| // converting said IP address to text | ||||
| #define TINY_GSM_CLIENT_CONNECT_OVERLOADS() \ | ||||
|   virtual int connect(IPAddress ip, uint16_t port, int timeout_s) { \ | ||||
|     String host; host.reserve(16); \ | ||||
|     host += ip[0]; \ | ||||
|     host += "."; \ | ||||
|     host += ip[1]; \ | ||||
|     host += "."; \ | ||||
|     host += ip[2]; \ | ||||
|     host += "."; \ | ||||
|     host += ip[3]; \ | ||||
|     return connect(host.c_str(), port, timeout_s); \ | ||||
|   } \ | ||||
|   virtual int connect(const char *host, uint16_t port) { \ | ||||
|     return connect(host, port, 75); \ | ||||
|   } \ | ||||
|   virtual int connect(IPAddress ip, uint16_t port) { \ | ||||
|     return connect(ip, port, 75); \ | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    * WiFi functions - only apply to WiFi modems | ||||
|    */ | ||||
|  | ||||
|   virtual bool networkConnect(const char* ssid, const char* pwd) { return false; } | ||||
|   virtual bool networkDisconnect() { return false; } | ||||
|  | ||||
|   /* | ||||
|    * GPRS functions - only apply to cellular modems | ||||
|    */ | ||||
|  | ||||
|   virtual bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { | ||||
|     return false; | ||||
|   } | ||||
|   virtual bool gprsDisconnect() { return false; } | ||||
|   virtual bool isGprsConnected() { return false; } | ||||
|  | ||||
|   /* | ||||
|    * IP Address functions | ||||
|    */ | ||||
|  | ||||
|   virtual String getLocalIP() = 0; | ||||
|   virtual IPAddress localIP() { | ||||
|     return TinyGsmIpFromString(getLocalIP()); | ||||
| // Writes data out on the client using the modem send functionality | ||||
| #define TINY_GSM_CLIENT_WRITE() \ | ||||
|   virtual size_t write(const uint8_t *buf, size_t size) { \ | ||||
|     TINY_GSM_YIELD(); \ | ||||
|     at->maintain(); \ | ||||
|     return at->modemSend(buf, size, mux); \ | ||||
|   } \ | ||||
|   \ | ||||
|   virtual size_t write(uint8_t c) {\ | ||||
|     return write(&c, 1); \ | ||||
|   }\ | ||||
|   \ | ||||
|   virtual size_t write(const char *str) { \ | ||||
|     if (str == NULL) return 0; \ | ||||
|     return write((const uint8_t *)str, strlen(str)); \ | ||||
|   } | ||||
|  | ||||
|     /* | ||||
|      Utilities | ||||
|      */ | ||||
|  | ||||
|     template<typename T> | ||||
|     void streamWrite(T last) { | ||||
|       stream.print(last); | ||||
|     } | ||||
|  | ||||
|     template<typename T, typename... Args> | ||||
|     void streamWrite(T head, Args... tail) { | ||||
|       stream.print(head); | ||||
|       streamWrite(tail...); | ||||
|     } | ||||
|  | ||||
|     bool streamSkipUntil(const char c, const unsigned long timeout = 1000L) { | ||||
|       unsigned long startMillis = millis(); | ||||
|       while (millis() - startMillis < timeout) { | ||||
|         while (millis() - startMillis < timeout && !stream.available()) { | ||||
|           TINY_GSM_YIELD(); | ||||
|         } | ||||
|         if (stream.read() == c) | ||||
|           return true; | ||||
|       } | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
| public: | ||||
|   Stream&       stream; | ||||
| }; | ||||
| // Returns the combined number of characters available in the TinyGSM fifo | ||||
| // and the modem chips internal fifo, doing an extra check-in with the | ||||
| // modem to see if anything has arrived without a UURC. | ||||
| #define TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK() \ | ||||
|   virtual int available() { \ | ||||
|     TINY_GSM_YIELD(); \ | ||||
|     if (!rx.size()) { \ | ||||
|       /* Workaround: sometimes module forgets to notify about data arrival. | ||||
|       TODO: Currently we ping the module periodically, | ||||
|       but maybe there's a better indicator that we need to poll */ \ | ||||
|       if (millis() - prev_check > 250) { \ | ||||
|         got_data = true; \ | ||||
|         prev_check = millis(); \ | ||||
|       } \ | ||||
|       at->maintain(); \ | ||||
|     } \ | ||||
|     return rx.size() + sock_available; \ | ||||
|   } | ||||
|  | ||||
|  | ||||
| // Returns the combined number of characters available in the TinyGSM fifo and | ||||
| // the modem chips internal fifo.  Use this if you don't expect to miss any URC's. | ||||
| #define TINY_GSM_CLIENT_AVAILABLE_NO_BUFFER_CHECK() \ | ||||
|   virtual int available() { \ | ||||
|     TINY_GSM_YIELD(); \ | ||||
|     if (!rx.size()) { \ | ||||
|       at->maintain(); \ | ||||
|     } \ | ||||
|     return rx.size() + sock_available; \ | ||||
|   } | ||||
|  | ||||
|  | ||||
| // Returns the number of characters avaialable in the TinyGSM fifo | ||||
| // Assumes the modem chip has no internal fifo | ||||
| #define TINY_GSM_CLIENT_AVAILABLE_NO_MODEM_FIFO() \ | ||||
|   virtual int available() { \ | ||||
|     TINY_GSM_YIELD(); \ | ||||
|     if (!rx.size() && sock_connected) { \ | ||||
|       at->maintain(); \ | ||||
|     } \ | ||||
|     return rx.size(); \ | ||||
|   } | ||||
|  | ||||
|  | ||||
| #define TINY_GSM_CLIENT_READ_OVERLOAD() \ | ||||
|   virtual int read() { \ | ||||
|     uint8_t c; \ | ||||
|     if (read(&c, 1) == 1) { \ | ||||
|       return c; \ | ||||
|     } \ | ||||
|     return -1; \ | ||||
|   } | ||||
|  | ||||
| // Reads characters out of the TinyGSM fifo, and from the modem chips internal | ||||
| // fifo if avaiable, also double checking with the modem if data has arrived | ||||
| // without issuing a UURC. | ||||
| #define TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK() \ | ||||
|   virtual int read(uint8_t *buf, size_t size) { \ | ||||
|     TINY_GSM_YIELD(); \ | ||||
|     at->maintain(); \ | ||||
|     size_t cnt = 0; \ | ||||
|     while (cnt < size) { \ | ||||
|       size_t chunk = TinyGsmMin(size-cnt, rx.size()); \ | ||||
|       if (chunk > 0) { \ | ||||
|         rx.get(buf, chunk); \ | ||||
|         buf += chunk; \ | ||||
|         cnt += chunk; \ | ||||
|         continue; \ | ||||
|       } \ | ||||
|       /* Workaround: sometimes module forgets to notify about data arrival. | ||||
|       TODO: Currently we ping the module periodically, | ||||
|       but maybe there's a better indicator that we need to poll */ \ | ||||
|       if (millis() - prev_check > 250) { \ | ||||
|         got_data = true; \ | ||||
|         prev_check = millis(); \ | ||||
|       } \ | ||||
|       /* TODO: Read directly into user buffer? */ \ | ||||
|       at->maintain(); \ | ||||
|       if (sock_available > 0) { \ | ||||
|         int n = at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux); \ | ||||
|         if (n == 0) break; \ | ||||
|       } else { \ | ||||
|         break; \ | ||||
|       } \ | ||||
|     } \ | ||||
|     return cnt; \ | ||||
|   } \ | ||||
|   TINY_GSM_CLIENT_READ_OVERLOAD() | ||||
|  | ||||
|  | ||||
| // Reads characters out of the TinyGSM fifo, and from the modem chips internal | ||||
| // fifo if avaiable.  Use this if you don't expect to miss any URC's. | ||||
| #define TINY_GSM_CLIENT_READ_NO_BUFFER_CHECK() \ | ||||
|   virtual int read(uint8_t *buf, size_t size) { \ | ||||
|     TINY_GSM_YIELD(); \ | ||||
|     at->maintain(); \ | ||||
|     size_t cnt = 0; \ | ||||
|     while (cnt < size) { \ | ||||
|       size_t chunk = TinyGsmMin(size-cnt, rx.size()); \ | ||||
|       if (chunk > 0) { \ | ||||
|         rx.get(buf, chunk); \ | ||||
|         buf += chunk; \ | ||||
|         cnt += chunk; \ | ||||
|         continue; \ | ||||
|       } \ | ||||
|       /* TODO: Read directly into user buffer? */ \ | ||||
|       at->maintain(); \ | ||||
|       if (sock_available > 0) { \ | ||||
|         int n = at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux); \ | ||||
|         if (n == 0) break; \ | ||||
|       } else { \ | ||||
|         break; \ | ||||
|       } \ | ||||
|     } \ | ||||
|     return cnt; \ | ||||
|   } \ | ||||
|   TINY_GSM_CLIENT_READ_OVERLOAD() | ||||
|  | ||||
|  | ||||
| // Reads characters out of the TinyGSM fifo, waiting for any URC's from the | ||||
| // modem for new data if there's nothing in the fifo.  This assumes the | ||||
| //modem chip itself has no fifo. | ||||
| #define TINY_GSM_CLIENT_READ_NO_MODEM_FIFO() \ | ||||
|   virtual int read(uint8_t *buf, size_t size) { \ | ||||
|     TINY_GSM_YIELD(); \ | ||||
|     size_t cnt = 0; \ | ||||
|     uint32_t _startMillis = millis(); \ | ||||
|     while (cnt < size && millis() - _startMillis < _timeout) { \ | ||||
|       size_t chunk = TinyGsmMin(size-cnt, rx.size()); \ | ||||
|       if (chunk > 0) { \ | ||||
|         rx.get(buf, chunk); \ | ||||
|         buf += chunk; \ | ||||
|         cnt += chunk; \ | ||||
|         continue; \ | ||||
|       } \ | ||||
|       /* TODO: Read directly into user buffer? */ \ | ||||
|       if (!rx.size() && sock_connected) { \ | ||||
|         at->maintain(); \ | ||||
|       } \ | ||||
|     } \ | ||||
|     return cnt; \ | ||||
|   } \ | ||||
|   \ | ||||
|   virtual int read() { \ | ||||
|     uint8_t c; \ | ||||
|     if (read(&c, 1) == 1) { \ | ||||
|       return c; \ | ||||
|     } \ | ||||
|     return -1; \ | ||||
|   } | ||||
|  | ||||
|  | ||||
| // The peek, flush, and connected functions | ||||
| #define TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() \ | ||||
|   virtual int peek() { return -1; } /* TODO */ \ | ||||
|   \ | ||||
|   virtual void flush() { at->stream.flush(); } \ | ||||
|   \ | ||||
|   virtual uint8_t connected() { \ | ||||
|     if (available()) { \ | ||||
|       return true; \ | ||||
|     } \ | ||||
|     return sock_connected; \ | ||||
|   } \ | ||||
|   virtual operator bool() { return connected(); } | ||||
|  | ||||
|  | ||||
| // Set baud rate via the V.25TER standard IPR command | ||||
| #define TINY_GSM_MODEM_SET_BAUD_IPR() \ | ||||
|   void setBaud(unsigned long baud) { \ | ||||
|     sendAT(GF("+IPR="), baud); \ | ||||
|   } | ||||
|  | ||||
|  | ||||
| // Test response to AT commands | ||||
| #define TINY_GSM_MODEM_TEST_AT() \ | ||||
|   bool testAT(unsigned long timeout_ms = 10000L) { \ | ||||
|     for (unsigned long start = millis(); millis() - start < timeout_ms; ) { \ | ||||
|       sendAT(GF("")); \ | ||||
|       if (waitResponse(200) == 1) return true; \ | ||||
|       delay(100); \ | ||||
|     } \ | ||||
|     return false; \ | ||||
|   } | ||||
|  | ||||
|  | ||||
| // Keeps listening for modem URC's and iterates through sockets | ||||
| // to see if any data is avaiable | ||||
| #define TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS() \ | ||||
|   void maintain() { \ | ||||
|     for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) { \ | ||||
|       GsmClient* sock = sockets[mux]; \ | ||||
|       if (sock && sock->got_data) { \ | ||||
|         sock->got_data = false; \ | ||||
|         sock->sock_available = modemGetAvailable(mux); \ | ||||
|       } \ | ||||
|     } \ | ||||
|     while (stream.available()) { \ | ||||
|       waitResponse(15, NULL, NULL); \ | ||||
|     } \ | ||||
|   } | ||||
|  | ||||
|  | ||||
| // Keeps listening for modem URC's - doesn't check socks because | ||||
| // modem has no internal fifo | ||||
| #define TINY_GSM_MODEM_MAINTAIN_LISTEN() \ | ||||
|   void maintain() { \ | ||||
|     waitResponse(10, NULL, NULL); \ | ||||
|   } | ||||
|  | ||||
|  | ||||
| // Asks for modem information via the V.25TER standard ATI command | ||||
| // NOTE:  The actual value and style of the response is quite varied | ||||
| #define TINY_GSM_MODEM_GET_INFO_ATI() \ | ||||
|   String getModemInfo() { \ | ||||
|     sendAT(GF("I")); \ | ||||
|     String res; \ | ||||
|     if (waitResponse(1000L, res) != 1) { \ | ||||
|       return ""; \ | ||||
|     } \ | ||||
|     res.replace(GSM_NL "OK" GSM_NL, ""); \ | ||||
|     res.replace(GSM_NL, " "); \ | ||||
|     res.trim(); \ | ||||
|     return res; \ | ||||
|   } | ||||
|  | ||||
|  | ||||
| // Unlocks a sim via the 3GPP TS command AT+CPIN | ||||
| #define TINY_GSM_MODEM_SIM_UNLOCK_CPIN() \ | ||||
|   bool simUnlock(const char *pin) { \ | ||||
|     sendAT(GF("+CPIN=\""), pin, GF("\"")); \ | ||||
|     return waitResponse() == 1; \ | ||||
|   } | ||||
|  | ||||
|  | ||||
| // Gets the CCID of a sim card via AT+CCID | ||||
| #define TINY_GSM_MODEM_GET_SIMCCID_CCID() \ | ||||
|   String getSimCCID() { \ | ||||
|     sendAT(GF("+CCID")); \ | ||||
|     if (waitResponse(GF(GSM_NL "+CCID:")) != 1) { \ | ||||
|       return ""; \ | ||||
|     } \ | ||||
|     String res = stream.readStringUntil('\n'); \ | ||||
|     waitResponse(); \ | ||||
|     res.trim(); \ | ||||
|     return res; \ | ||||
|   } | ||||
|  | ||||
|  | ||||
| // Asks for TA Serial Number Identification (IMEI) via the V.25TER standard AT+GSN command | ||||
| #define TINY_GSM_MODEM_GET_IMEI_GSN() \ | ||||
|   String getIMEI() { \ | ||||
|     sendAT(GF("+GSN")); \ | ||||
|     if (waitResponse(GF(GSM_NL)) != 1) { \ | ||||
|       return ""; \ | ||||
|     } \ | ||||
|     String res = stream.readStringUntil('\n'); \ | ||||
|     waitResponse(); \ | ||||
|     res.trim(); \ | ||||
|     return res; \ | ||||
|   } | ||||
|  | ||||
|  | ||||
| // Gets the modem's registration status via CREG/CGREG/CEREG | ||||
| // CREG = Generic network registration | ||||
| // CGREG = GPRS service registration | ||||
| // CEREG = EPS registration for LTE modules | ||||
| #define TINY_GSM_MODEM_GET_REGISTRATION_XREG(regCommand) \ | ||||
|   RegStatus getRegistrationStatus() { \ | ||||
|     sendAT(GF("+" #regCommand "?")); \ | ||||
|     if (waitResponse(GF(GSM_NL "+" #regCommand ":")) != 1) { \ | ||||
|       return REG_UNKNOWN; \ | ||||
|     } \ | ||||
|     streamSkipUntil(','); /* Skip format (0) */ \ | ||||
|     int status = stream.readStringUntil('\n').toInt(); \ | ||||
|     waitResponse(); \ | ||||
|     return (RegStatus)status; \ | ||||
|   } | ||||
|  | ||||
|  | ||||
| // Gets the current network operator via the 3GPP TS command AT+COPS | ||||
| #define TINY_GSM_MODEM_GET_OPERATOR_COPS() \ | ||||
|   String getOperator() { \ | ||||
|     sendAT(GF("+COPS?")); \ | ||||
|     if (waitResponse(GF(GSM_NL "+COPS:")) != 1) { \ | ||||
|       return ""; \ | ||||
|     } \ | ||||
|     streamSkipUntil('"'); /* Skip mode and format */ \ | ||||
|     String res = stream.readStringUntil('"'); \ | ||||
|     waitResponse(); \ | ||||
|     return res; \ | ||||
|   } | ||||
|  | ||||
|  | ||||
| // Waits for network attachment | ||||
| #define TINY_GSM_MODEM_WAIT_FOR_NETWORK() \ | ||||
|   bool waitForNetwork(unsigned long timeout_ms = 60000L) { \ | ||||
|     for (unsigned long start = millis(); millis() - start < timeout_ms; ) { \ | ||||
|       if (isNetworkConnected()) { \ | ||||
|         return true; \ | ||||
|       } \ | ||||
|       delay(250); \ | ||||
|     } \ | ||||
|     return false; \ | ||||
|   } | ||||
|  | ||||
|  | ||||
| // Checks if current attached to GPRS/EPS service | ||||
| #define TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() \ | ||||
|   bool isGprsConnected() { \ | ||||
|     sendAT(GF("+CGATT?")); \ | ||||
|     if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) { \ | ||||
|       return false; \ | ||||
|     } \ | ||||
|     int res = stream.readStringUntil('\n').toInt(); \ | ||||
|     waitResponse(); \ | ||||
|     if (res != 1) \ | ||||
|       return false; \ | ||||
|   \ | ||||
|     return localIP() != IPAddress(0,0,0,0); \ | ||||
|   } | ||||
|  | ||||
|  | ||||
| // Gets signal quality report according to 3GPP TS command AT+CSQ | ||||
| #define TINY_GSM_MODEM_GET_CSQ() \ | ||||
|   int16_t getSignalQuality() { \ | ||||
|     sendAT(GF("+CSQ")); \ | ||||
|     if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) { \ | ||||
|       return 99; \ | ||||
|     } \ | ||||
|     int res = stream.readStringUntil(',').toInt(); \ | ||||
|     waitResponse(); \ | ||||
|     return res; \ | ||||
|   } | ||||
|  | ||||
|  | ||||
| // Yields up to a time-out period and then reads a character from the stream into the mux FIFO | ||||
| // TODO:  Do we need to wait two _timeout periods for no character return?  Will wait once in the first | ||||
| // "while !stream.available()" and then will wait again in the stream.read() function. | ||||
| #define TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT \ | ||||
|   uint32_t startMillis = millis(); \ | ||||
|   while (!stream.available() && (millis() - startMillis < sockets[mux]->_timeout)) { TINY_GSM_YIELD(); } \ | ||||
|   char c = stream.read(); \ | ||||
|   sockets[mux]->rx.put(c); | ||||
|  | ||||
|  | ||||
| // Utility templates for writing/skipping characters on a stream | ||||
| #define TINY_GSM_MODEM_STREAM_UTILITIES() \ | ||||
|   template<typename T> \ | ||||
|   void streamWrite(T last) { \ | ||||
|     stream.print(last); \ | ||||
|   } \ | ||||
|   \ | ||||
|   template<typename T, typename... Args> \ | ||||
|   void streamWrite(T head, Args... tail) { \ | ||||
|     stream.print(head); \ | ||||
|     streamWrite(tail...); \ | ||||
|   } \ | ||||
|   \ | ||||
|   template<typename... Args> \ | ||||
|   void sendAT(Args... cmd) { \ | ||||
|     streamWrite("AT", cmd..., GSM_NL); \ | ||||
|     stream.flush(); \ | ||||
|     TINY_GSM_YIELD(); \ | ||||
|     /* DBG("### AT:", cmd...); */ \ | ||||
|   } \ | ||||
|   \ | ||||
|   bool streamSkipUntil(const char c, const unsigned long timeout_ms = 1000L) { \ | ||||
|     unsigned long startMillis = millis(); \ | ||||
|     while (millis() - startMillis < timeout_ms) { \ | ||||
|       while (millis() - startMillis < timeout_ms && !stream.available()) { \ | ||||
|         TINY_GSM_YIELD(); \ | ||||
|       } \ | ||||
|       if (stream.read() == c) { \ | ||||
|         return true; \ | ||||
|       } \ | ||||
|     } \ | ||||
|     return false; \ | ||||
|   } | ||||
|  | ||||
|  | ||||
| #endif | ||||
|   | ||||
		Reference in New Issue
	
	Block a user