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 |     - PLATFORMIO_CI_SRC=tools/FactoryReset | ||||||
|  |  | ||||||
|     # Arduino test |     # 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_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 --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" | ||||||
|     - 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_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 --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" | ||||||
|     - 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_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 --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" | ||||||
|     - 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_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 --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" | ||||||
|     - 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_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 --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" | ||||||
|     - 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_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 |     # 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" |     - 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_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_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_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_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 |     # 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" |     #- 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 | ## 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** | **Data connections** | ||||||
| TCP (HTTP, MQTT, Blynk, ...) | ✔      | ✔      | ✔         | ✔         | ✔        | ✔       | ✔          | ✔         | ✔             |         | | TCP (HTTP, MQTT, Blynk, ...) | ✔      | ✔      | ✔         | ✔         | ✔        | ✔       | ✔          | ✔         | ✔             | ✔       | ✔       | | ||||||
| UDP                          | ◌      | ◌      |           |           |          | ◌       | ◌          |           |               | ◌       | | UDP                          | ◌      | ◌      |           |           |          | ◌       | ◌          |           |               | ◌       | ◌       | | ||||||
| SSL/TLS (HTTPS)              | ✔¹     | ✔      | x         | x         | ✔        | ✔       | ◌          |           |               | ◌       | | SSL/TLS (HTTPS)              | ✔¹     | ✔      | x         | x         | ✔        | ✔       | ◌          |           |               | ◌       | ✔       | | ||||||
| **USSD** | **USSD** | ||||||
| Sending USSD requests        | ✔      |        | ✔         | ✔         | x        |          |             |         |               | ✔       | | Sending USSD requests        | ✔      |        | ✔         | ✔         | x        |          |             |         |               | ✔       |         | | ||||||
| Decoding 7,8,16-bit response | ✔      |        | ✔         | ✔         | x        |          |             |         |               | ✔       | | Decoding 7,8,16-bit response | ✔      |        | ✔         | ✔         | x        |          |             |         |               | ✔       |         | | ||||||
| **SMS** | **SMS** | ||||||
| Sending                      | ✔      | ✔      | ✔         | ✔         | x        | ✔        | ✔          | ✔        | ✔             | ✔       | | Sending                      | ✔      | ✔      | ✔         | ✔         | x        | ✔        | ✔          | ✔        | ✔             | ✔       | ✔       | | ||||||
| Sending Unicode              | ✔      |        | ◌         | x         | x        |          | ✔           | ✔       | ✔             | ✔       | | Sending Unicode              | ✔      |        | ◌         | x         | x        |          | ✔           | ✔       | ✔             | ✔       |         | | ||||||
| Reading                      |        |        |           |           | x        |          |             |         |               |         | | Reading                      |        |        |           |           | x        |          |             |         |               |         |         | | ||||||
| Incoming message event       |        |        |           | ?         | x        |          |             |         |               |         | | Incoming message event       |        |        |           | ?         | x        |          |             |         |               |         |         | | ||||||
| **Calls** | **Calls** | ||||||
| Dial, hangup                 | ✔      |        | ✔         | x         | x        | x        |             |         |               |         | | Dial, hangup                 | ✔      |        | ✔         | x         | x        | x        |             |         |               |         |         | | ||||||
| Receiving calls              | ✔      |        | ✔         | x         | x        | x        |             |         |               |         | | Receiving calls              | ✔      |        | ✔         | x         | x        | x        |             |         |               |         |         | | ||||||
| Incoming event (RING)        | ◌      |        | ◌         | x         | x        | x        |             |         |               |         | | Incoming event (RING)        | ◌      |        | ◌         | x         | x        | x        |             |         |               |         |         | | ||||||
| DTMF sending                 | ✔      |        | ✔         | x         | x        | x        |             |         |               |         | | DTMF sending                 | ✔      |        | ✔         | x         | x        | x        |             |         |               |         |         | | ||||||
| DTMF decoding                | ◌      |        | x         | x         | x        | x        |             |         |               |         | | DTMF decoding                | ◌      |        | x         | x         | x        | x        |             |         |               |         |         | | ||||||
| **Location** | **Location** | ||||||
| GSM location service         | ✔      | ✔      | x         | x         | x        | x        |             | x       | ✔             | ✔       | | GSM location service         | ✔      | ✔      | x         | x         | x        | x        |             | x       | ✔             | ✔       | x       | | ||||||
| GPS/GNSS                     | ✔¹     | x      | ◌¹        | x         | x        | x        |             | x       |               | ✔       | | GPS/GNSS                     | ✔¹     | x      | ◌¹        | x         | x        | x        |             | x       |               | ✔       | x       | | ||||||
| **Credits** | **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 | ✔ - implemented  ◌ - planned  x - not available on this modem | ||||||
| ¹ - only some device models or firmware revisions have this feature (SIM8xx R14.18, A7, etc.) | ¹ - 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) | - Digi XBee WiFi and Cellular (using XBee command mode) | ||||||
| - Neoway M590 | - 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_) | - 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 BG96 ***(alpha)*** | ||||||
| - Quectel M95 ***(alpha)*** | - Quectel M95 ***(alpha)*** | ||||||
| - Quectel MC60 ***(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). | - ... 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: | More modems may be supported later: | ||||||
| - [ ] Sequans Monarch LTE Cat M1/NB1 |  | ||||||
| - [ ] Quectel M10, UG95 | - [ ] Quectel M10, UG95 | ||||||
| - [ ] SIMCom SIM5320, SIM5360, SIM5216 | - [ ] SIMCom SIM5320, SIM5360, SIM5216 | ||||||
|  | - [ ] SIMCom SIM7020, SIM7100 | ||||||
| - [ ] Telit GL865 | - [ ] Telit GL865 | ||||||
| - [ ] ZTE MG2639 | - [ ] ZTE MG2639 | ||||||
| - [ ] Hi-Link HLK-RM04 | - [ ] Hi-Link HLK-RM04 | ||||||
|   | |||||||
| @@ -10,11 +10,13 @@ | |||||||
|  **************************************************************/ |  **************************************************************/ | ||||||
|  |  | ||||||
| // Select your modem: | // Select your modem: | ||||||
| //#define TINY_GSM_MODEM_SIM800 | #define TINY_GSM_MODEM_SIM800 | ||||||
| // #define TINY_GSM_MODEM_SIM808 | // #define TINY_GSM_MODEM_SIM808 | ||||||
|  | // #define TINY_GSM_MODEM_SIM868 | ||||||
| // #define TINY_GSM_MODEM_SIM900 | // #define TINY_GSM_MODEM_SIM900 | ||||||
| #define TINY_GSM_MODEM_SIM7000 | // #define TINY_GSM_MODEM_SIM7000 | ||||||
| // #define TINY_GSM_MODEM_UBLOX | // #define TINY_GSM_MODEM_UBLOX | ||||||
|  | // #define TINY_GSM_MODEM_SARAR4 | ||||||
| // #define TINY_GSM_MODEM_M95 | // #define TINY_GSM_MODEM_M95 | ||||||
| // #define TINY_GSM_MODEM_BG96 | // #define TINY_GSM_MODEM_BG96 | ||||||
| // #define TINY_GSM_MODEM_A6 | // #define TINY_GSM_MODEM_A6 | ||||||
| @@ -24,8 +26,9 @@ | |||||||
| // #define TINY_GSM_MODEM_MC60E | // #define TINY_GSM_MODEM_MC60E | ||||||
| // #define TINY_GSM_MODEM_ESP8266 | // #define TINY_GSM_MODEM_ESP8266 | ||||||
| // #define TINY_GSM_MODEM_XBEE | // #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 | #define SerialMon Serial | ||||||
|  |  | ||||||
| // Set serial for AT commands (to the module) | // Set serial for AT commands (to the module) | ||||||
| @@ -36,10 +39,13 @@ | |||||||
| //#include <SoftwareSerial.h> | //#include <SoftwareSerial.h> | ||||||
| //SoftwareSerial SerialAT(2, 3); // RX, TX | //SoftwareSerial SerialAT(2, 3); // RX, TX | ||||||
|  |  | ||||||
|  | // See all AT commands, if wanted | ||||||
| //#define DUMP_AT_COMMANDS | //#define DUMP_AT_COMMANDS | ||||||
|  |  | ||||||
|  | // See the debugging, if wanted | ||||||
| #define TINY_GSM_DEBUG SerialMon | #define TINY_GSM_DEBUG SerialMon | ||||||
|  |  | ||||||
|  | // Range to attempt to autobaud | ||||||
| #define GSM_AUTOBAUD_MIN 9600 | #define GSM_AUTOBAUD_MIN 9600 | ||||||
| #define GSM_AUTOBAUD_MAX 38400 | #define GSM_AUTOBAUD_MAX 38400 | ||||||
|  |  | ||||||
| @@ -47,6 +53,7 @@ | |||||||
|  * Test enabled |  * Test enabled | ||||||
|  */ |  */ | ||||||
| #define TINY_GSM_USE_GPRS true | #define TINY_GSM_USE_GPRS true | ||||||
|  | #define TINY_GSM_USE_WIFI false | ||||||
| #define TINY_GSM_USE_CALL true | #define TINY_GSM_USE_CALL true | ||||||
| #define TINY_GSM_USE_SMS true | #define TINY_GSM_USE_SMS true | ||||||
| #define TINY_GSM_USE_USSD true | #define TINY_GSM_USE_USSD true | ||||||
| @@ -65,6 +72,8 @@ | |||||||
| const char apn[]  = "YourAPN"; | const char apn[]  = "YourAPN"; | ||||||
| const char user[] = ""; | const char user[] = ""; | ||||||
| const char pass[] = ""; | const char pass[] = ""; | ||||||
|  | const char wifiSSID[]  = "YourSSID"; | ||||||
|  | const char wifiPass[] = "SSIDpw"; | ||||||
|  |  | ||||||
| #include <TinyGsmClient.h> | #include <TinyGsmClient.h> | ||||||
|  |  | ||||||
| @@ -82,11 +91,18 @@ void setup() { | |||||||
|   delay(10); |   delay(10); | ||||||
|  |  | ||||||
|   // Set your reset, enable, power pins here |   // Set your reset, enable, power pins here | ||||||
|  |   pinMode(20, OUTPUT); | ||||||
|  |   digitalWrite(20, HIGH); | ||||||
|  |  | ||||||
|  |   pinMode(23, OUTPUT); | ||||||
|  |   digitalWrite(23, HIGH); | ||||||
|  |  | ||||||
|  |   DBG("Wait..."); | ||||||
|   delay(3000); |   delay(3000); | ||||||
|  |  | ||||||
|   // Set GSM module baud rate |   // Set GSM module baud rate | ||||||
|   TinyGsmAutoBaud(SerialAT,GSM_AUTOBAUD_MIN,GSM_AUTOBAUD_MAX); |   TinyGsmAutoBaud(SerialAT,GSM_AUTOBAUD_MIN,GSM_AUTOBAUD_MAX); | ||||||
|  |   // SerialAT.begin(9600); | ||||||
| } | } | ||||||
|  |  | ||||||
| void loop() { | void loop() { | ||||||
| @@ -95,6 +111,7 @@ void loop() { | |||||||
|   // To skip it, call init() instead of restart() |   // To skip it, call init() instead of restart() | ||||||
|   DBG("Initializing modem..."); |   DBG("Initializing modem..."); | ||||||
|   if (!modem.restart()) { |   if (!modem.restart()) { | ||||||
|  |   // if (!modem.init()) { | ||||||
|     DBG("Failed to restart modem, delaying 10s and retrying"); |     DBG("Failed to restart modem, delaying 10s and retrying"); | ||||||
|     delay(3000); |     delay(3000); | ||||||
|     // restart autobaud in case GSM just rebooted |     // restart autobaud in case GSM just rebooted | ||||||
| @@ -106,10 +123,22 @@ void loop() { | |||||||
|   String modemInfo = modem.getModemInfo(); |   String modemInfo = modem.getModemInfo(); | ||||||
|   DBG("Modem:", modemInfo); |   DBG("Modem:", modemInfo); | ||||||
|  |  | ||||||
|  | #if TINY_GSM_USE_GPRS | ||||||
|   // Unlock your SIM card with a PIN if needed |   // Unlock your SIM card with a PIN if needed | ||||||
|   if ( GSM_PIN && modem.getSimStatus() != 3 ) { |   if ( GSM_PIN && modem.getSimStatus() != 3 ) { | ||||||
|     modem.simUnlock(GSM_PIN); |     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..."); |   DBG("Waiting for network..."); | ||||||
|   if (!modem.waitForNetwork()) { |   if (!modem.waitForNetwork()) { | ||||||
| @@ -221,6 +250,11 @@ void loop() { | |||||||
|   } |   } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | #if TINY_GSM_USE_WIFI | ||||||
|  |   modem.networkDisconnect(); | ||||||
|  |   DBG("WiFi disconnected"); | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #if TINY_GSM_POWERDOWN | #if TINY_GSM_POWERDOWN | ||||||
|   // Try to power-off (modem may decide to restart automatically) |   // Try to power-off (modem may decide to restart automatically) | ||||||
|   // To turn off modem completely, please use Reset/Enable pins |   // To turn off modem completely, please use Reset/Enable pins | ||||||
|   | |||||||
| @@ -30,11 +30,12 @@ | |||||||
|  |  | ||||||
| // Select your modem: | // Select your modem: | ||||||
| #define TINY_GSM_MODEM_SIM800 | #define TINY_GSM_MODEM_SIM800 | ||||||
| // #define TINY_GSM_MODEM_SIM900 |  | ||||||
| // #define TINY_GSM_MODEM_SIM808 | // #define TINY_GSM_MODEM_SIM808 | ||||||
| // #define TINY_GSM_MODEM_SIM868 | // #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_UBLOX | ||||||
|  | // #define TINY_GSM_MODEM_SARAR4 | ||||||
| // #define TINY_GSM_MODEM_M95 | // #define TINY_GSM_MODEM_M95 | ||||||
| // #define TINY_GSM_MODEM_BG96 | // #define TINY_GSM_MODEM_BG96 | ||||||
| // #define TINY_GSM_MODEM_A6 | // #define TINY_GSM_MODEM_A6 | ||||||
| @@ -44,6 +45,7 @@ | |||||||
| // #define TINY_GSM_MODEM_MC60E | // #define TINY_GSM_MODEM_MC60E | ||||||
| // #define TINY_GSM_MODEM_ESP8266 | // #define TINY_GSM_MODEM_ESP8266 | ||||||
| // #define TINY_GSM_MODEM_XBEE | // #define TINY_GSM_MODEM_XBEE | ||||||
|  | // #define TINY_GSM_MODEM_SEQUANS_MONARCH | ||||||
|  |  | ||||||
| #include <TinyGsmClient.h> | #include <TinyGsmClient.h> | ||||||
| #include <BlynkSimpleSIM800.h> | #include <BlynkSimpleSIM800.h> | ||||||
|   | |||||||
| @@ -15,11 +15,12 @@ | |||||||
|  |  | ||||||
| // Select your modem: | // Select your modem: | ||||||
| #define TINY_GSM_MODEM_SIM800 | #define TINY_GSM_MODEM_SIM800 | ||||||
| // #define TINY_GSM_MODEM_SIM900 |  | ||||||
| // #define TINY_GSM_MODEM_SIM808 | // #define TINY_GSM_MODEM_SIM808 | ||||||
| // #define TINY_GSM_MODEM_SIM868 | // #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_UBLOX | ||||||
|  | // #define TINY_GSM_MODEM_SARAR4 | ||||||
| // #define TINY_GSM_MODEM_M95 | // #define TINY_GSM_MODEM_M95 | ||||||
| // #define TINY_GSM_MODEM_BG96 | // #define TINY_GSM_MODEM_BG96 | ||||||
| // #define TINY_GSM_MODEM_A6 | // #define TINY_GSM_MODEM_A6 | ||||||
| @@ -29,6 +30,7 @@ | |||||||
| // #define TINY_GSM_MODEM_MC60E | // #define TINY_GSM_MODEM_MC60E | ||||||
| // #define TINY_GSM_MODEM_ESP8266 | // #define TINY_GSM_MODEM_ESP8266 | ||||||
| // #define TINY_GSM_MODEM_XBEE | // #define TINY_GSM_MODEM_XBEE | ||||||
|  | // #define TINY_GSM_MODEM_SEQUANS_MONARCH | ||||||
|  |  | ||||||
| // Increase RX buffer if needed | // Increase RX buffer if needed | ||||||
| #define TINY_GSM_RX_BUFFER 1024 | #define TINY_GSM_RX_BUFFER 1024 | ||||||
|   | |||||||
| @@ -19,11 +19,12 @@ | |||||||
|  |  | ||||||
| // Select your modem: | // Select your modem: | ||||||
| #define TINY_GSM_MODEM_SIM800 | #define TINY_GSM_MODEM_SIM800 | ||||||
| // #define TINY_GSM_MODEM_SIM900 |  | ||||||
| // #define TINY_GSM_MODEM_SIM808 | // #define TINY_GSM_MODEM_SIM808 | ||||||
| // #define TINY_GSM_MODEM_SIM868 | // #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_UBLOX | ||||||
|  | // #define TINY_GSM_MODEM_SARAR4 | ||||||
| // #define TINY_GSM_MODEM_M95 | // #define TINY_GSM_MODEM_M95 | ||||||
| // #define TINY_GSM_MODEM_BG96 | // #define TINY_GSM_MODEM_BG96 | ||||||
| // #define TINY_GSM_MODEM_A6 | // #define TINY_GSM_MODEM_A6 | ||||||
| @@ -32,6 +33,8 @@ | |||||||
| // #define TINY_GSM_MODEM_MC60 | // #define TINY_GSM_MODEM_MC60 | ||||||
| // #define TINY_GSM_MODEM_MC60E | // #define TINY_GSM_MODEM_MC60E | ||||||
| // #define TINY_GSM_MODEM_ESP8266 | // #define TINY_GSM_MODEM_ESP8266 | ||||||
|  | // #define TINY_GSM_MODEM_XBEE | ||||||
|  | // #define TINY_GSM_MODEM_SEQUANS_MONARCH | ||||||
|  |  | ||||||
| // Increase RX buffer to capture the entire response | // Increase RX buffer to capture the entire response | ||||||
| // Chips without internal buffering (A6/A7, ESP8266, M590) | // Chips without internal buffering (A6/A7, ESP8266, M590) | ||||||
| @@ -39,6 +42,9 @@ | |||||||
| // else data will be lost (and the http library will fail). | // else data will be lost (and the http library will fail). | ||||||
| #define TINY_GSM_RX_BUFFER 650 | #define TINY_GSM_RX_BUFFER 650 | ||||||
|  |  | ||||||
|  | // See all AT commands, if wanted | ||||||
|  | //#define DUMP_AT_COMMANDS | ||||||
|  |  | ||||||
| // See the debugging, if wanted | // See the debugging, if wanted | ||||||
| //#define TINY_GSM_DEBUG Serial | //#define TINY_GSM_DEBUG Serial | ||||||
| //#define LOGGING | //#define LOGGING | ||||||
| @@ -49,12 +55,10 @@ | |||||||
| #include <TinyGsmClient.h> | #include <TinyGsmClient.h> | ||||||
| #include <ArduinoHttpClient.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) | // Set serial for debug console (to the Serial Monitor, default speed 115200) | ||||||
| #define SerialMon Serial | #define SerialMon Serial | ||||||
|  |  | ||||||
|  | // Set serial for AT commands (to the module) | ||||||
| // Use Hardware Serial on Mega, Leonardo, Micro | // Use Hardware Serial on Mega, Leonardo, Micro | ||||||
| #define SerialAT Serial1 | #define SerialAT Serial1 | ||||||
|  |  | ||||||
| @@ -62,6 +66,11 @@ | |||||||
| //#include <SoftwareSerial.h> | //#include <SoftwareSerial.h> | ||||||
| //SoftwareSerial SerialAT(2, 3); // RX, TX | //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 | // Your GPRS credentials | ||||||
| // Leave empty, if missing user or pass | // Leave empty, if missing user or pass | ||||||
| @@ -91,7 +100,15 @@ void setup() { | |||||||
|   // Set console baud rate |   // Set console baud rate | ||||||
|   SerialMon.begin(115200); |   SerialMon.begin(115200); | ||||||
|   delay(10); |   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 |   // Set GSM module baud rate | ||||||
|   SerialAT.begin(115200); |   SerialAT.begin(115200); | ||||||
| @@ -99,11 +116,11 @@ void setup() { | |||||||
|  |  | ||||||
|   // Restart takes quite some time |   // Restart takes quite some time | ||||||
|   // To skip it, call init() instead of restart() |   // To skip it, call init() instead of restart() | ||||||
|   SerialMon.println(F("Initializing modem...")); |   SerialMon.println("Initializing modem..."); | ||||||
|   modem.restart(); |   modem.restart(); | ||||||
|  |  | ||||||
|   String modemInfo = modem.getModemInfo(); |   String modemInfo = modem.getModemInfo(); | ||||||
|   SerialMon.print(F("Modem: ")); |   SerialMon.print("Modem: "); | ||||||
|   SerialMon.println(modemInfo); |   SerialMon.println(modemInfo); | ||||||
|  |  | ||||||
|   // Unlock your SIM card with a PIN |   // Unlock your SIM card with a PIN | ||||||
| @@ -112,17 +129,17 @@ void setup() { | |||||||
|  |  | ||||||
| void loop() { | void loop() { | ||||||
|  |  | ||||||
|   if (modem.hasWifi()) { | #if TINY_GSM_USE_WIFI | ||||||
|     SerialMon.print(F("Setting SSID/password...")); |   SerialMon.print(F("Setting SSID/password...")); | ||||||
|     if (!modem.networkConnect(wifiSSID, wifiPass)) { |   if (!modem.networkConnect(wifiSSID, wifiPass)) { | ||||||
|       SerialMon.println(" fail"); |     SerialMon.println(" fail"); | ||||||
|       delay(10000); |     delay(10000); | ||||||
|       return; |     return; | ||||||
|     } |  | ||||||
|     SerialMon.println(" OK"); |  | ||||||
|   } |   } | ||||||
|  |   SerialMon.println(" OK"); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|   SerialMon.print(F("Waiting for network...")); |   SerialMon.print("Waiting for network..."); | ||||||
|   if (!modem.waitForNetwork()) { |   if (!modem.waitForNetwork()) { | ||||||
|     SerialMon.println(" fail"); |     SerialMon.println(" fail"); | ||||||
|     delay(10000); |     delay(10000); | ||||||
| @@ -130,7 +147,11 @@ void loop() { | |||||||
|   } |   } | ||||||
|   SerialMon.println(" OK"); |   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(F("Connecting to ")); | ||||||
|     SerialMon.print(apn); |     SerialMon.print(apn); | ||||||
|     if (!modem.gprsConnect(apn, gprsUser, gprsPass)) { |     if (!modem.gprsConnect(apn, gprsUser, gprsPass)) { | ||||||
| @@ -139,7 +160,7 @@ void loop() { | |||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|     SerialMon.println(" OK"); |     SerialMon.println(" OK"); | ||||||
|   } | #endif | ||||||
|  |  | ||||||
|   SerialMon.print(F("Performing HTTP GET request... ")); |   SerialMon.print(F("Performing HTTP GET request... ")); | ||||||
|   int err = http.get(resource); |   int err = http.get(resource); | ||||||
| @@ -185,8 +206,14 @@ void loop() { | |||||||
|   http.stop(); |   http.stop(); | ||||||
|   SerialMon.println(F("Server disconnected")); |   SerialMon.println(F("Server disconnected")); | ||||||
|  |  | ||||||
|   modem.gprsDisconnect(); | #if TINY_GSM_USE_WIFI | ||||||
|   SerialMon.println(F("GPRS disconnected")); |     modem.networkDisconnect(); | ||||||
|  |     SerialMon.println(F("WiFi disconnected")); | ||||||
|  | #endif | ||||||
|  | #if TINY_GSM_USE_GPRS | ||||||
|  |     modem.gprsDisconnect(); | ||||||
|  |     SerialMon.println(F("GPRS disconnected")); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|   // Do nothing forevermore |   // Do nothing forevermore | ||||||
|   while (true) { |   while (true) { | ||||||
|   | |||||||
| @@ -21,6 +21,7 @@ | |||||||
| // #define TINY_GSM_MODEM_SIM808 | // #define TINY_GSM_MODEM_SIM808 | ||||||
| // #define TINY_GSM_MODEM_SIM868 | // #define TINY_GSM_MODEM_SIM868 | ||||||
| // #define TINY_GSM_MODEM_UBLOX | // #define TINY_GSM_MODEM_UBLOX | ||||||
|  | // #define TINY_GSM_MODEM_SARAR4 | ||||||
| // #define TINY_GSM_MODEM_ESP8266 | // #define TINY_GSM_MODEM_ESP8266 | ||||||
|  |  | ||||||
| // Increase RX buffer to capture the entire response | // Increase RX buffer to capture the entire response | ||||||
| @@ -29,6 +30,9 @@ | |||||||
| // else data will be lost (and the http library will fail). | // else data will be lost (and the http library will fail). | ||||||
| #define TINY_GSM_RX_BUFFER 650 | #define TINY_GSM_RX_BUFFER 650 | ||||||
|  |  | ||||||
|  | // See all AT commands, if wanted | ||||||
|  | //#define DUMP_AT_COMMANDS | ||||||
|  |  | ||||||
| // See the debugging, if wanted | // See the debugging, if wanted | ||||||
| //#define TINY_GSM_DEBUG Serial | //#define TINY_GSM_DEBUG Serial | ||||||
| //#define LOGGING | //#define LOGGING | ||||||
| @@ -39,12 +43,10 @@ | |||||||
| #include <TinyGsmClient.h> | #include <TinyGsmClient.h> | ||||||
| #include <ArduinoHttpClient.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) | // Set serial for debug console (to the Serial Monitor, default speed 115200) | ||||||
| #define SerialMon Serial | #define SerialMon Serial | ||||||
|  |  | ||||||
|  | // Set serial for AT commands (to the module) | ||||||
| // Use Hardware Serial on Mega, Leonardo, Micro | // Use Hardware Serial on Mega, Leonardo, Micro | ||||||
| #define SerialAT Serial1 | #define SerialAT Serial1 | ||||||
|  |  | ||||||
| @@ -52,6 +54,11 @@ | |||||||
| //#include <SoftwareSerial.h> | //#include <SoftwareSerial.h> | ||||||
| //SoftwareSerial SerialAT(2, 3); // RX, TX | //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 | // Your GPRS credentials | ||||||
| // Leave empty, if missing user or pass | // Leave empty, if missing user or pass | ||||||
| @@ -81,7 +88,15 @@ void setup() { | |||||||
|   // Set console baud rate |   // Set console baud rate | ||||||
|   SerialMon.begin(115200); |   SerialMon.begin(115200); | ||||||
|   delay(10); |   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 |   // Set GSM module baud rate | ||||||
|   SerialAT.begin(115200); |   SerialAT.begin(115200); | ||||||
| @@ -89,11 +104,11 @@ void setup() { | |||||||
|  |  | ||||||
|   // Restart takes quite some time |   // Restart takes quite some time | ||||||
|   // To skip it, call init() instead of restart() |   // To skip it, call init() instead of restart() | ||||||
|   SerialMon.println(F("Initializing modem...")); |   SerialMon.println("Initializing modem..."); | ||||||
|   modem.restart(); |   modem.restart(); | ||||||
|  |  | ||||||
|   String modemInfo = modem.getModemInfo(); |   String modemInfo = modem.getModemInfo(); | ||||||
|   SerialMon.print(F("Modem: ")); |   SerialMon.print("Modem: "); | ||||||
|   SerialMon.println(modemInfo); |   SerialMon.println(modemInfo); | ||||||
|  |  | ||||||
|   // Unlock your SIM card with a PIN |   // Unlock your SIM card with a PIN | ||||||
| @@ -107,17 +122,17 @@ void setup() { | |||||||
|  |  | ||||||
| void loop() { | void loop() { | ||||||
|  |  | ||||||
|   if (modem.hasWifi()) { | #if TINY_GSM_USE_WIFI | ||||||
|     SerialMon.print(F("Setting SSID/password...")); |   SerialMon.print(F("Setting SSID/password...")); | ||||||
|     if (!modem.networkConnect(wifiSSID, wifiPass)) { |   if (!modem.networkConnect(wifiSSID, wifiPass)) { | ||||||
|       SerialMon.println(" fail"); |     SerialMon.println(" fail"); | ||||||
|       delay(10000); |     delay(10000); | ||||||
|       return; |     return; | ||||||
|     } |  | ||||||
|     SerialMon.println(" OK"); |  | ||||||
|   } |   } | ||||||
|  |   SerialMon.println(" OK"); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|   SerialMon.print(F("Waiting for network...")); |   SerialMon.print("Waiting for network..."); | ||||||
|   if (!modem.waitForNetwork()) { |   if (!modem.waitForNetwork()) { | ||||||
|     SerialMon.println(" fail"); |     SerialMon.println(" fail"); | ||||||
|     delay(10000); |     delay(10000); | ||||||
| @@ -125,7 +140,11 @@ void loop() { | |||||||
|   } |   } | ||||||
|   SerialMon.println(" OK"); |   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(F("Connecting to ")); | ||||||
|     SerialMon.print(apn); |     SerialMon.print(apn); | ||||||
|     if (!modem.gprsConnect(apn, gprsUser, gprsPass)) { |     if (!modem.gprsConnect(apn, gprsUser, gprsPass)) { | ||||||
| @@ -134,7 +153,7 @@ void loop() { | |||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|     SerialMon.println(" OK"); |     SerialMon.println(" OK"); | ||||||
|   } | #endif | ||||||
|  |  | ||||||
|   SerialMon.print(F("Performing HTTPS GET request... ")); |   SerialMon.print(F("Performing HTTPS GET request... ")); | ||||||
|   http.connectionKeepAlive(); // Currently, this is needed for HTTPS |   http.connectionKeepAlive(); // Currently, this is needed for HTTPS | ||||||
| @@ -181,8 +200,14 @@ void loop() { | |||||||
|   http.stop(); |   http.stop(); | ||||||
|   SerialMon.println(F("Server disconnected")); |   SerialMon.println(F("Server disconnected")); | ||||||
|  |  | ||||||
|   modem.gprsDisconnect(); | #if TINY_GSM_USE_WIFI | ||||||
|   SerialMon.println(F("GPRS disconnected")); |     modem.networkDisconnect(); | ||||||
|  |     SerialMon.println(F("WiFi disconnected")); | ||||||
|  | #endif | ||||||
|  | #if TINY_GSM_USE_GPRS | ||||||
|  |     modem.gprsDisconnect(); | ||||||
|  |     SerialMon.println(F("GPRS disconnected")); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|   // Do nothing forevermore |   // Do nothing forevermore | ||||||
|   while (true) { |   while (true) { | ||||||
|   | |||||||
| @@ -29,11 +29,12 @@ | |||||||
|  |  | ||||||
| // Select your modem: | // Select your modem: | ||||||
| #define TINY_GSM_MODEM_SIM800 | #define TINY_GSM_MODEM_SIM800 | ||||||
| // #define TINY_GSM_MODEM_SIM900 |  | ||||||
| // #define TINY_GSM_MODEM_SIM808 | // #define TINY_GSM_MODEM_SIM808 | ||||||
| // #define TINY_GSM_MODEM_SIM868 | // #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_UBLOX | ||||||
|  | // #define TINY_GSM_MODEM_SARAR4 | ||||||
| // #define TINY_GSM_MODEM_M95 | // #define TINY_GSM_MODEM_M95 | ||||||
| // #define TINY_GSM_MODEM_BG96 | // #define TINY_GSM_MODEM_BG96 | ||||||
| // #define TINY_GSM_MODEM_A6 | // #define TINY_GSM_MODEM_A6 | ||||||
| @@ -43,6 +44,7 @@ | |||||||
| // #define TINY_GSM_MODEM_MC60E | // #define TINY_GSM_MODEM_MC60E | ||||||
| // #define TINY_GSM_MODEM_ESP8266 | // #define TINY_GSM_MODEM_ESP8266 | ||||||
| // #define TINY_GSM_MODEM_XBEE | // #define TINY_GSM_MODEM_XBEE | ||||||
|  | // #define TINY_GSM_MODEM_SEQUANS_MONARCH | ||||||
|  |  | ||||||
| #include <TinyGsmClient.h> | #include <TinyGsmClient.h> | ||||||
| #include <PubSubClient.h> | #include <PubSubClient.h> | ||||||
|   | |||||||
| @@ -10,11 +10,12 @@ | |||||||
|  |  | ||||||
| // Select your modem: | // Select your modem: | ||||||
| #define TINY_GSM_MODEM_SIM800 | #define TINY_GSM_MODEM_SIM800 | ||||||
| // #define TINY_GSM_MODEM_SIM900 |  | ||||||
| // #define TINY_GSM_MODEM_SIM808 | // #define TINY_GSM_MODEM_SIM808 | ||||||
| // #define TINY_GSM_MODEM_SIM868 | // #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_UBLOX | ||||||
|  | // #define TINY_GSM_MODEM_SARAR4 | ||||||
| // #define TINY_GSM_MODEM_M95 | // #define TINY_GSM_MODEM_M95 | ||||||
| // #define TINY_GSM_MODEM_BG96 | // #define TINY_GSM_MODEM_BG96 | ||||||
| // #define TINY_GSM_MODEM_A6 | // #define TINY_GSM_MODEM_A6 | ||||||
| @@ -24,21 +25,13 @@ | |||||||
| // #define TINY_GSM_MODEM_MC60E | // #define TINY_GSM_MODEM_MC60E | ||||||
| // #define TINY_GSM_MODEM_ESP8266 | // #define TINY_GSM_MODEM_ESP8266 | ||||||
| // #define TINY_GSM_MODEM_XBEE | // #define TINY_GSM_MODEM_XBEE | ||||||
|  | // #define TINY_GSM_MODEM_SEQUANS_MONARCH | ||||||
|  |  | ||||||
| // Increase RX buffer if needed | // Increase RX buffer if needed | ||||||
| //#define TINY_GSM_RX_BUFFER 512 | // #define TINY_GSM_RX_BUFFER 512 | ||||||
|  |  | ||||||
| // See the debugging, if wanted |  | ||||||
| //#define TINY_GSM_DEBUG Serial |  | ||||||
| //#define LOGGING |  | ||||||
|  |  | ||||||
| // Add a reception delay, if needed | // Add a reception delay, if needed | ||||||
| //#define TINY_GSM_YIELD() { delay(1); } | // #define TINY_GSM_YIELD() { delay(1); } | ||||||
|  |  | ||||||
| #include <TinyGsmClient.h> |  | ||||||
|  |  | ||||||
| // Uncomment this if you want to see all AT commands |  | ||||||
| //#define DUMP_AT_COMMANDS |  | ||||||
|  |  | ||||||
| // Uncomment this if you want to use SSL | // Uncomment this if you want to use SSL | ||||||
| //#define USE_SSL | //#define USE_SSL | ||||||
| @@ -46,6 +39,7 @@ | |||||||
| // Set serial for debug console (to the Serial Monitor, default speed 115200) | // Set serial for debug console (to the Serial Monitor, default speed 115200) | ||||||
| #define SerialMon Serial | #define SerialMon Serial | ||||||
|  |  | ||||||
|  | // Set serial for AT commands (to the module) | ||||||
| // Use Hardware Serial on Mega, Leonardo, Micro | // Use Hardware Serial on Mega, Leonardo, Micro | ||||||
| #define SerialAT Serial1 | #define SerialAT Serial1 | ||||||
|  |  | ||||||
| @@ -53,6 +47,21 @@ | |||||||
| //#include <SoftwareSerial.h> | //#include <SoftwareSerial.h> | ||||||
| //SoftwareSerial SerialAT(2, 3); // RX, TX | //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 | // Your GPRS credentials | ||||||
| // Leave empty, if missing user or pass | // Leave empty, if missing user or pass | ||||||
| @@ -66,6 +75,8 @@ const char wifiPass[] = "SSIDpw"; | |||||||
| const char server[] = "vsh.pp.ua"; | const char server[] = "vsh.pp.ua"; | ||||||
| const char resource[] = "/TinyGSM/logo.txt"; | const char resource[] = "/TinyGSM/logo.txt"; | ||||||
|  |  | ||||||
|  | #include <TinyGsmClient.h> | ||||||
|  |  | ||||||
| #ifdef DUMP_AT_COMMANDS | #ifdef DUMP_AT_COMMANDS | ||||||
|   #include <StreamDebugger.h> |   #include <StreamDebugger.h> | ||||||
|   StreamDebugger debugger(SerialAT, SerialMon); |   StreamDebugger debugger(SerialAT, SerialMon); | ||||||
| @@ -86,22 +97,29 @@ void setup() { | |||||||
|   // Set console baud rate |   // Set console baud rate | ||||||
|   SerialMon.begin(115200); |   SerialMon.begin(115200); | ||||||
|   delay(10); |   delay(10); | ||||||
|   SerialMon.println(F("Wait...")); |  | ||||||
|  |   // Set your reset, enable, power pins here | ||||||
|  |   pinMode(20, OUTPUT); | ||||||
|  |   digitalWrite(20, HIGH); | ||||||
|  |  | ||||||
|   pinMode(23, OUTPUT); |   pinMode(23, OUTPUT); | ||||||
|   digitalWrite(23, LOW); |   digitalWrite(23, HIGH); | ||||||
|  |  | ||||||
|  |   SerialMon.println("Wait..."); | ||||||
|  |  | ||||||
|   // Set GSM module baud rate |   // Set GSM module baud rate | ||||||
|  |   // TinyGsmAutoBaud(SerialAT,GSM_AUTOBAUD_MIN,GSM_AUTOBAUD_MAX); | ||||||
|   SerialAT.begin(9600); |   SerialAT.begin(9600); | ||||||
|   delay(3000); |   delay(3000); | ||||||
|  |  | ||||||
|   // Restart takes quite some time |   // Restart takes quite some time | ||||||
|   // To skip it, call init() instead of restart() |   // To skip it, call init() instead of restart() | ||||||
|   SerialMon.println(F("Initializing modem...")); |   SerialMon.println("Initializing modem..."); | ||||||
|   modem.restart(); |   modem.restart(); | ||||||
|  |   // modem.init(); | ||||||
|  |  | ||||||
|   String modemInfo = modem.getModemInfo(); |   String modemInfo = modem.getModemInfo(); | ||||||
|   SerialMon.print(F("Modem: ")); |   SerialMon.print("Modem: "); | ||||||
|   SerialMon.println(modemInfo); |   SerialMon.println(modemInfo); | ||||||
|  |  | ||||||
|   // Unlock your SIM card with a PIN |   // Unlock your SIM card with a PIN | ||||||
| @@ -110,46 +128,41 @@ void setup() { | |||||||
|  |  | ||||||
| void loop() { | void loop() { | ||||||
|  |  | ||||||
|   if (modem.hasWifi()) { | #if TINY_GSM_USE_WIFI | ||||||
|     SerialMon.print(F("Setting SSID/password...")); |   SerialMon.print(F("Setting SSID/password...")); | ||||||
|     if (!modem.networkConnect(wifiSSID, wifiPass)) { |   if (!modem.networkConnect(wifiSSID, wifiPass)) { | ||||||
|       SerialMon.println(" fail"); |     SerialMon.println(" fail"); | ||||||
|       delay(10000); |     delay(10000); | ||||||
|       return; |     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"); |  | ||||||
|   } |   } | ||||||
|  |   SerialMon.println(" OK"); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|   SerialMon.print(F("Waiting for network...")); |   SerialMon.print("Waiting for network..."); | ||||||
|   if (!modem.waitForNetwork()) { |   if (!modem.waitForNetwork(240000L)) { | ||||||
|     SerialMon.println(" fail"); |     SerialMon.println(" fail"); | ||||||
|     delay(10000); |     delay(10000); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|   SerialMon.println(" OK"); |   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(F("Connecting to ")); | ||||||
|     SerialMon.print(apn); |     SerialMon.println(apn); | ||||||
|     if (!modem.gprsConnect(apn, gprsUser, gprsPass)) { |     if (!modem.gprsConnect(apn, gprsUser, gprsPass)) { | ||||||
|       SerialMon.println(" fail"); |       SerialMon.println(" fail"); | ||||||
|       delay(10000); |       delay(10000); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|     SerialMon.println(" OK"); |     SerialMon.println(" OK"); | ||||||
|   } | #endif | ||||||
|  |  | ||||||
|   SerialMon.print(F("Connecting to ")); |   SerialMon.print("Connecting to "); | ||||||
|   SerialMon.print(server); |   SerialMon.println(server); | ||||||
|   if (!client.connect(server, port)) { |   if (!client.connect(server, port)) { | ||||||
|     SerialMon.println(" fail"); |     SerialMon.println(" fail"); | ||||||
|     delay(10000); |     delay(10000); | ||||||
| @@ -158,6 +171,7 @@ void loop() { | |||||||
|   SerialMon.println(" OK"); |   SerialMon.println(" OK"); | ||||||
|  |  | ||||||
|   // Make a HTTP GET request: |   // Make a HTTP GET request: | ||||||
|  |   SerialMon.println("Performing HTTP GET request..."); | ||||||
|   client.print(String("GET ") + resource + " HTTP/1.0\r\n"); |   client.print(String("GET ") + resource + " HTTP/1.0\r\n"); | ||||||
|   client.print(String("Host: ") + server + "\r\n"); |   client.print(String("Host: ") + server + "\r\n"); | ||||||
|   client.print("Connection: close\r\n\r\n"); |   client.print("Connection: close\r\n\r\n"); | ||||||
| @@ -178,14 +192,14 @@ void loop() { | |||||||
|   client.stop(); |   client.stop(); | ||||||
|   SerialMon.println(F("Server disconnected")); |   SerialMon.println(F("Server disconnected")); | ||||||
|  |  | ||||||
|   if (modem.hasWifi()) { | #if TINY_GSM_USE_WIFI | ||||||
|     modem.networkDisconnect(); |     modem.networkDisconnect(); | ||||||
|     SerialMon.println(F("WiFi disconnected")); |     SerialMon.println(F("WiFi disconnected")); | ||||||
|   } | #endif | ||||||
|   else { | #if TINY_GSM_USE_GPRS | ||||||
|     modem.gprsDisconnect(); |     modem.gprsDisconnect(); | ||||||
|     SerialMon.println(F("GPRS disconnected")); |     SerialMon.println(F("GPRS disconnected")); | ||||||
|   } | #endif | ||||||
|  |  | ||||||
|   // Do nothing forevermore |   // Do nothing forevermore | ||||||
|   while (true) { |   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", |   "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.", |   "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", |   "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": |   "authors": | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| name=TinyGSM | name=TinyGSM | ||||||
| version=0.6.2 | version=0.7.4 | ||||||
| author=Volodymyr Shymanskyy | author=Volodymyr Shymanskyy | ||||||
| maintainer=Volodymyr Shymanskyy | maintainer=Volodymyr Shymanskyy | ||||||
| sentence=A small Arduino library for GPRS modules, that just works. | sentence=A small Arduino library for GPRS modules, that just works. | ||||||
|   | |||||||
| @@ -48,6 +48,14 @@ | |||||||
|   typedef TinyGsmUBLOX::GsmClient TinyGsmClient; |   typedef TinyGsmUBLOX::GsmClient TinyGsmClient; | ||||||
|   typedef TinyGsmUBLOX::GsmClientSecure TinyGsmClientSecure; |   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) | #elif defined(TINY_GSM_MODEM_M95) | ||||||
|   #define TINY_GSM_MODEM_HAS_GPRS |   #define TINY_GSM_MODEM_HAS_GPRS | ||||||
|   #include <TinyGsmClientM95.h> |   #include <TinyGsmClientM95.h> | ||||||
| @@ -96,6 +104,15 @@ | |||||||
|   typedef TinyGsmXBee::GsmClient TinyGsmClient; |   typedef TinyGsmXBee::GsmClient TinyGsmClient; | ||||||
|   typedef TinyGsmXBee::GsmClientSecure TinyGsmClientSecure; |   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 | #else | ||||||
|   #error "Please define GSM modem model" |   #error "Please define GSM modem model" | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -12,6 +12,10 @@ | |||||||
|  |  | ||||||
| //#define TINY_GSM_DEBUG Serial | //#define TINY_GSM_DEBUG Serial | ||||||
|  |  | ||||||
|  | #if !defined(TINY_GSM_RX_BUFFER) | ||||||
|  |   #define TINY_GSM_RX_BUFFER 256 | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #define TINY_GSM_MUX_COUNT 8 | #define TINY_GSM_MUX_COUNT 8 | ||||||
|  |  | ||||||
| #include <TinyGsmCommon.h> | #include <TinyGsmCommon.h> | ||||||
| @@ -36,7 +40,7 @@ enum RegStatus { | |||||||
| }; | }; | ||||||
|  |  | ||||||
|  |  | ||||||
| class TinyGsmA6 : public TinyGsmModem | class TinyGsmA6 | ||||||
| { | { | ||||||
|  |  | ||||||
| public: | public: | ||||||
| @@ -58,40 +62,24 @@ public: | |||||||
|     this->mux = -1; |     this->mux = -1; | ||||||
|     sock_connected = false; |     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; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| public: | public: | ||||||
|   virtual int connect(const char *host, uint16_t port) { |   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||||
|     stop(); |     stop(); | ||||||
|     TINY_GSM_YIELD(); |     TINY_GSM_YIELD(); | ||||||
|     rx.clear(); |     rx.clear(); | ||||||
|     uint8_t newMux = -1; |     uint8_t newMux = -1; | ||||||
|     sock_connected = at->modemConnect(host, port, &newMux); |     sock_connected = at->modemConnect(host, port, &newMux, timeout_s); | ||||||
|     if (sock_connected) { |     if (sock_connected) { | ||||||
|       mux = newMux; |       mux = newMux; | ||||||
|       at->sockets[mux] = this; |       at->sockets[mux] = this; | ||||||
|     // ^^ TODO: attach the socet after attempting connection or above at init? |  | ||||||
|     // Currently done inconsistently between modems |  | ||||||
|     } |     } | ||||||
|     return sock_connected; |     return sock_connected; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   virtual int connect(IPAddress ip, uint16_t port) { | TINY_GSM_CLIENT_CONNECT_OVERLOADS() | ||||||
|     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); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual void stop() { |   virtual void stop() { | ||||||
|     TINY_GSM_YIELD(); |     TINY_GSM_YIELD(); | ||||||
| @@ -101,67 +89,13 @@ public: | |||||||
|     rx.clear(); |     rx.clear(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   virtual size_t write(const uint8_t *buf, size_t size) { | TINY_GSM_CLIENT_WRITE() | ||||||
|     TINY_GSM_YIELD(); |  | ||||||
|     //at->maintain(); |  | ||||||
|     return at->modemSend(buf, size, mux); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual size_t write(uint8_t c) { | TINY_GSM_CLIENT_AVAILABLE_NO_MODEM_FIFO() | ||||||
|     return write(&c, 1); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual size_t write(const char *str) { | TINY_GSM_CLIENT_READ_NO_MODEM_FIFO() | ||||||
|     if (str == NULL) return 0; |  | ||||||
|     return write((const uint8_t *)str, strlen(str)); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual int available() { | TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() | ||||||
|     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(); } |  | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Extended API |    * Extended API | ||||||
| @@ -180,7 +114,7 @@ private: | |||||||
| public: | public: | ||||||
|  |  | ||||||
|   TinyGsmA6(Stream& stream) |   TinyGsmA6(Stream& stream) | ||||||
|     : TinyGsmModem(stream), stream(stream) |     : stream(stream) | ||||||
|   { |   { | ||||||
|     memset(sockets, 0, sizeof(sockets)); |     memset(sockets, 0, sizeof(sockets)); | ||||||
|   } |   } | ||||||
| @@ -189,6 +123,10 @@ public: | |||||||
|    * Basic functions |    * Basic functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|  |    bool begin(const char* pin = NULL) { | ||||||
|  |      return init(pin); | ||||||
|  |    } | ||||||
|  |  | ||||||
|   bool init(const char* pin = NULL) { |   bool init(const char* pin = NULL) { | ||||||
|     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); |     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); | ||||||
|     if (!testAT()) { |     if (!testAT()) { | ||||||
| @@ -216,22 +154,11 @@ public: | |||||||
|     return "AI-Thinker A6"; |     return "AI-Thinker A6"; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   void setBaud(unsigned long baud) { | TINY_GSM_MODEM_SET_BAUD_IPR() | ||||||
|     sendAT(GF("+IPR="), baud); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool testAT(unsigned long timeout = 10000L) { | TINY_GSM_MODEM_TEST_AT() | ||||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { |  | ||||||
|       sendAT(GF("")); |  | ||||||
|       if (waitResponse(200) == 1) return true; |  | ||||||
|       delay(100); |  | ||||||
|     } |  | ||||||
|     return false; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   void maintain() { | TINY_GSM_MODEM_MAINTAIN_LISTEN() | ||||||
|     waitResponse(10, NULL, NULL); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool factoryDefault() { |   bool factoryDefault() { | ||||||
|     sendAT(GF("&FZE0&W"));  // Factory + Reset + Echo Off + Write |     sendAT(GF("&FZE0&W"));  // Factory + Reset + Echo Off + Write | ||||||
| @@ -240,17 +167,7 @@ public: | |||||||
|     return waitResponse() == 1; |     return waitResponse() == 1; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   String getModemInfo() { | TINY_GSM_MODEM_GET_INFO_ATI() | ||||||
|     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; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool hasSSL() { |   bool hasSSL() { | ||||||
|     return false; |     return false; | ||||||
| @@ -290,10 +207,7 @@ public: | |||||||
|    * SIM card functions |    * SIM card functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   bool simUnlock(const char *pin) { | TINY_GSM_MODEM_SIM_UNLOCK_CPIN() | ||||||
|     sendAT(GF("+CPIN=\""), pin, GF("\"")); |  | ||||||
|     return waitResponse() == 1; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   String getSimCCID() { |   String getSimCCID() { | ||||||
|     sendAT(GF("+CCID")); |     sendAT(GF("+CCID")); | ||||||
| @@ -306,19 +220,10 @@ public: | |||||||
|     return res; |     return res; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   String getIMEI() { | TINY_GSM_MODEM_GET_IMEI_GSN() | ||||||
|     sendAT(GF("+GSN")); |  | ||||||
|     if (waitResponse(GF(GSM_NL)) != 1) { |  | ||||||
|       return ""; |  | ||||||
|     } |  | ||||||
|     String res = stream.readStringUntil('\n'); |  | ||||||
|     waitResponse(); |  | ||||||
|     res.trim(); |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SimStatus getSimStatus(unsigned long timeout = 10000L) { |   SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { | ||||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { |     for (unsigned long start = millis(); millis() - start < timeout_ms; ) { | ||||||
|       sendAT(GF("+CPIN?")); |       sendAT(GF("+CPIN?")); | ||||||
|       if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { |       if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { | ||||||
|         delay(1000); |         delay(1000); | ||||||
| @@ -336,16 +241,7 @@ public: | |||||||
|     return SIM_ERROR; |     return SIM_ERROR; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   RegStatus getRegistrationStatus() { | TINY_GSM_MODEM_GET_REGISTRATION_XREG(CREG) | ||||||
|     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; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   String getOperator() { |   String getOperator() { | ||||||
|     sendAT(GF("+COPS=3,0")); // Set format |     sendAT(GF("+COPS=3,0")); // Set format | ||||||
| @@ -365,24 +261,19 @@ public: | |||||||
|    * Generic network functions |    * Generic network functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   int16_t getSignalQuality() { | TINY_GSM_MODEM_GET_CSQ() | ||||||
|     sendAT(GF("+CSQ")); |  | ||||||
|     if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) { |  | ||||||
|       return 99; |  | ||||||
|     } |  | ||||||
|     int res = stream.readStringUntil(',').toInt(); |  | ||||||
|     waitResponse(); |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool isNetworkConnected() { |   bool isNetworkConnected() { | ||||||
|     RegStatus s = getRegistrationStatus(); |     RegStatus s = getRegistrationStatus(); | ||||||
|     return (s == REG_OK_HOME || s == REG_OK_ROAMING); |     return (s == REG_OK_HOME || s == REG_OK_ROAMING); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | TINY_GSM_MODEM_WAIT_FOR_NETWORK() | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * GPRS functions |    * GPRS functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { |   bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { | ||||||
|     gprsDisconnect(); |     gprsDisconnect(); | ||||||
|  |  | ||||||
| @@ -454,6 +345,10 @@ public: | |||||||
|     return res; |     return res; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   IPAddress localIP() { | ||||||
|  |     return TinyGsmIpFromString(getLocalIP()); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Phone Call functions |    * Phone Call functions | ||||||
|    */ |    */ | ||||||
| @@ -587,7 +482,7 @@ public: | |||||||
|   String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE; |   String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Battery functions |    * Battery & temperature functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE; |   uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||||
| @@ -597,27 +492,58 @@ public: | |||||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { |     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
|     stream.readStringUntil(','); |     streamSkipUntil(','); // Skip battery charge status | ||||||
|  |     // Read battery charge level | ||||||
|     int res = stream.readStringUntil('\n').toInt(); |     int res = stream.readStringUntil('\n').toInt(); | ||||||
|  |     // Wait for final OK | ||||||
|     waitResponse(); |     waitResponse(); | ||||||
|     return res; |     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 |    * Client related functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
| protected: | 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) { | ||||||
|     sendAT(GF("+CIPSTART="),  GF("\"TCP"), GF("\",\""), host, GF("\","), port); |     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; |       return false; | ||||||
|     } |     } | ||||||
|     int newMux = stream.readStringUntil('\n').toInt(); |     int newMux = stream.readStringUntil('\n').toInt(); | ||||||
|  |  | ||||||
|     int rsp = waitResponse(75000L, |     int rsp = waitResponse((timeout_ms- (millis() - startMillis)), | ||||||
|                            GF("CONNECT OK" GSM_NL), |                            GF("CONNECT OK" GSM_NL), | ||||||
|                            GF("CONNECT FAIL" GSM_NL), |                            GF("CONNECT FAIL" GSM_NL), | ||||||
|                            GF("ALREADY CONNECT" GSM_NL)); |                            GF("ALREADY CONNECT" GSM_NL)); | ||||||
| @@ -655,16 +581,10 @@ public: | |||||||
|    Utilities |    Utilities | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   template<typename... Args> | TINY_GSM_MODEM_STREAM_UTILITIES() | ||||||
|   void sendAT(Args... cmd) { |  | ||||||
|     streamWrite("AT", cmd..., GSM_NL); |  | ||||||
|     stream.flush(); |  | ||||||
|     TINY_GSM_YIELD(); |  | ||||||
|     //DBG("### AT:", cmd...); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // TODO: Optimize this! |   // 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 r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) |                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||||
|   { |   { | ||||||
| @@ -708,8 +628,7 @@ public: | |||||||
|             DBG("### Got: ", len, "->", sockets[mux]->rx.free()); |             DBG("### Got: ", len, "->", sockets[mux]->rx.free()); | ||||||
|           } |           } | ||||||
|           while (len--) { |           while (len--) { | ||||||
|             while (!stream.available()) { TINY_GSM_YIELD(); } |             TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT | ||||||
|             sockets[mux]->rx.put(stream.read()); |  | ||||||
|           } |           } | ||||||
|           if (len_orig > sockets[mux]->available()) { // TODO |           if (len_orig > sockets[mux]->available()) { // TODO | ||||||
|             DBG("### Fewer characters received than expected: ", sockets[mux]->available(), " vs ", len_orig); |             DBG("### Fewer characters received than expected: ", sockets[mux]->available(), " vs ", len_orig); | ||||||
| @@ -724,7 +643,7 @@ public: | |||||||
|           DBG("### Closed: ", mux); |           DBG("### Closed: ", mux); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } while (millis() - startMillis < timeout); |     } while (millis() - startMillis < timeout_ms); | ||||||
| finish: | finish: | ||||||
|     if (!index) { |     if (!index) { | ||||||
|       data.trim(); |       data.trim(); | ||||||
| @@ -737,12 +656,12 @@ finish: | |||||||
|     return index; |     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 r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) |                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||||
|   { |   { | ||||||
|     String data; |     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), |   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_DEBUG Serial | ||||||
| //#define TINY_GSM_USE_HEX | //#define TINY_GSM_USE_HEX | ||||||
|  |  | ||||||
|  | #if !defined(TINY_GSM_RX_BUFFER) | ||||||
|  |   #define TINY_GSM_RX_BUFFER 64 | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #define TINY_GSM_MUX_COUNT 12 | #define TINY_GSM_MUX_COUNT 12 | ||||||
|  |  | ||||||
| #include <TinyGsmCommon.h> | #include <TinyGsmCommon.h> | ||||||
| @@ -37,7 +41,7 @@ enum RegStatus { | |||||||
| }; | }; | ||||||
|  |  | ||||||
|  |  | ||||||
| class TinyGsmBG96 : public TinyGsmModem | class TinyGsmBG96 | ||||||
| { | { | ||||||
|  |  | ||||||
| public: | public: | ||||||
| @@ -62,36 +66,20 @@ public: | |||||||
|     got_data = false; |     got_data = false; | ||||||
|  |  | ||||||
|     at->sockets[mux] = this; |     at->sockets[mux] = this; | ||||||
|     //  ^^ TODO: attach the socket here at init?  Or later at connect? |  | ||||||
|     // Currently done inconsistently between modems |  | ||||||
|  |  | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| public: | public: | ||||||
|   virtual int connect(const char *host, uint16_t port) { |   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||||
|     stop(); |     stop(); | ||||||
|     TINY_GSM_YIELD(); |     TINY_GSM_YIELD(); | ||||||
|     rx.clear(); |     rx.clear(); | ||||||
|     sock_connected = at->modemConnect(host, port, mux); |     sock_connected = at->modemConnect(host, port, mux, false, timeout_s); | ||||||
|     // 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 |  | ||||||
|     return sock_connected; |     return sock_connected; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   virtual int connect(IPAddress ip, uint16_t port) { | TINY_GSM_CLIENT_CONNECT_OVERLOADS() | ||||||
|     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); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual void stop() { |   virtual void stop() { | ||||||
|     TINY_GSM_YIELD(); |     TINY_GSM_YIELD(); | ||||||
| @@ -112,70 +100,13 @@ public: | |||||||
|     at->waitResponse(); |     at->waitResponse(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   virtual size_t write(const uint8_t *buf, size_t size) { | TINY_GSM_CLIENT_WRITE() | ||||||
|     TINY_GSM_YIELD(); |  | ||||||
|     at->maintain(); |  | ||||||
|     return at->modemSend(buf, size, mux); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual size_t write(uint8_t c) { | TINY_GSM_CLIENT_AVAILABLE_NO_BUFFER_CHECK() | ||||||
|     return write(&c, 1); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual size_t write(const char *str) { | TINY_GSM_CLIENT_READ_NO_BUFFER_CHECK() | ||||||
|     if (str == NULL) return 0; |  | ||||||
|     return write((const uint8_t *)str, strlen(str)); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual int available() { | TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() | ||||||
|     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(); } |  | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Extended API |    * Extended API | ||||||
| @@ -203,11 +134,11 @@ private: | |||||||
| //   {} | //   {} | ||||||
| // | // | ||||||
| // public: | // public: | ||||||
| //   virtual int connect(const char *host, uint16_t port) { | //   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||||
| //     stop(); | //     stop(); | ||||||
| //     TINY_GSM_YIELD(); | //     TINY_GSM_YIELD(); | ||||||
| //     rx.clear(); | //     rx.clear(); | ||||||
| //     sock_connected = at->modemConnect(host, port, mux, true); | //     sock_connected = at->modemConnect(host, port, mux, true, timeout_s); | ||||||
| //     return sock_connected; | //     return sock_connected; | ||||||
| //   } | //   } | ||||||
| // }; | // }; | ||||||
| @@ -216,7 +147,7 @@ private: | |||||||
| public: | public: | ||||||
|  |  | ||||||
|   TinyGsmBG96(Stream& stream) |   TinyGsmBG96(Stream& stream) | ||||||
|     : TinyGsmModem(stream), stream(stream) |     : stream(stream) | ||||||
|   { |   { | ||||||
|     memset(sockets, 0, sizeof(sockets)); |     memset(sockets, 0, sizeof(sockets)); | ||||||
|   } |   } | ||||||
| @@ -225,6 +156,10 @@ public: | |||||||
|    * Basic functions |    * Basic functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|  |   bool begin(const char* pin = NULL) { | ||||||
|  |     return init(pin); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   bool init(const char* pin = NULL) { |   bool init(const char* pin = NULL) { | ||||||
|     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); |     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); | ||||||
|     if (!testAT()) { |     if (!testAT()) { | ||||||
| @@ -243,31 +178,11 @@ public: | |||||||
|     return "Quectel BG96"; |     return "Quectel BG96"; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   void setBaud(unsigned long baud) { | TINY_GSM_MODEM_SET_BAUD_IPR() | ||||||
|     sendAT(GF("+IPR="), baud); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool testAT(unsigned long timeout = 10000L) { | TINY_GSM_MODEM_TEST_AT() | ||||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { |  | ||||||
|       sendAT(GF("")); |  | ||||||
|       if (waitResponse(200) == 1) return true; |  | ||||||
|       delay(100); |  | ||||||
|     } |  | ||||||
|     return false; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   void maintain() { | TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS() | ||||||
|     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); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool factoryDefault() { |   bool factoryDefault() { | ||||||
|     sendAT(GF("&FZE0&W"));  // Factory + Reset + Echo Off + Write |     sendAT(GF("&FZE0&W"));  // Factory + Reset + Echo Off + Write | ||||||
| @@ -278,17 +193,7 @@ public: | |||||||
|     return waitResponse() == 1; |     return waitResponse() == 1; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   String getModemInfo() { | TINY_GSM_MODEM_GET_INFO_ATI() | ||||||
|     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; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool hasSSL() { |   bool hasSSL() { | ||||||
|     return false;  // TODO: For now |     return false;  // TODO: For now | ||||||
| @@ -337,14 +242,11 @@ public: | |||||||
|    * SIM card functions |    * SIM card functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   bool simUnlock(const char *pin) { | TINY_GSM_MODEM_SIM_UNLOCK_CPIN() | ||||||
|     sendAT(GF("+CPIN=\""), pin, GF("\"")); |  | ||||||
|     return waitResponse() == 1; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   String getSimCCID() { |   String getSimCCID() { | ||||||
|     sendAT(GF("+ICCID")); |     sendAT(GF("+QCCID")); | ||||||
|     if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) { |     if (waitResponse(GF(GSM_NL "+QCCID:")) != 1) { | ||||||
|       return ""; |       return ""; | ||||||
|     } |     } | ||||||
|     String res = stream.readStringUntil('\n'); |     String res = stream.readStringUntil('\n'); | ||||||
| @@ -353,19 +255,10 @@ public: | |||||||
|     return res; |     return res; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   String getIMEI() { | TINY_GSM_MODEM_GET_IMEI_GSN() | ||||||
|     sendAT(GF("+GSN")); |  | ||||||
|     if (waitResponse(GF(GSM_NL)) != 1) { |  | ||||||
|       return ""; |  | ||||||
|     } |  | ||||||
|     String res = stream.readStringUntil('\n'); |  | ||||||
|     waitResponse(); |  | ||||||
|     res.trim(); |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SimStatus getSimStatus(unsigned long timeout = 10000L) { |   SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { | ||||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { |     for (unsigned long start = millis(); millis() - start < timeout_ms; ) { | ||||||
|       sendAT(GF("+CPIN?")); |       sendAT(GF("+CPIN?")); | ||||||
|       if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { |       if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { | ||||||
|         delay(1000); |         delay(1000); | ||||||
| @@ -383,50 +276,27 @@ public: | |||||||
|     return SIM_ERROR; |     return SIM_ERROR; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   RegStatus getRegistrationStatus() { | TINY_GSM_MODEM_GET_REGISTRATION_XREG(CREG) | ||||||
|     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; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   String getOperator() { | TINY_GSM_MODEM_GET_OPERATOR_COPS() | ||||||
|     sendAT(GF("+COPS?")); |  | ||||||
|     if (waitResponse(GF(GSM_NL "+COPS:")) != 1) { |  | ||||||
|       return ""; |  | ||||||
|     } |  | ||||||
|     streamSkipUntil('"'); // Skip mode and format |  | ||||||
|     String res = stream.readStringUntil('"'); |  | ||||||
|     waitResponse(); |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Generic network functions |    * Generic network functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   int16_t getSignalQuality() { | TINY_GSM_MODEM_GET_CSQ() | ||||||
|     sendAT(GF("+CSQ")); |  | ||||||
|     if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) { |  | ||||||
|       return 99; |  | ||||||
|     } |  | ||||||
|     int res = stream.readStringUntil(',').toInt(); |  | ||||||
|     waitResponse(); |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool isNetworkConnected() { |   bool isNetworkConnected() { | ||||||
|     RegStatus s = getRegistrationStatus(); |     RegStatus s = getRegistrationStatus(); | ||||||
|     return (s == REG_OK_HOME || s == REG_OK_ROAMING); |     return (s == REG_OK_HOME || s == REG_OK_ROAMING); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | TINY_GSM_MODEM_WAIT_FOR_NETWORK() | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * GPRS functions |    * GPRS functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { |   bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { | ||||||
|     gprsDisconnect(); |     gprsDisconnect(); | ||||||
|  |  | ||||||
| @@ -459,18 +329,7 @@ public: | |||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool isGprsConnected() { | TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() | ||||||
|     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); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * IP Address functions |    * IP Address functions | ||||||
| @@ -486,6 +345,10 @@ public: | |||||||
|     return res; |     return res; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   IPAddress localIP() { | ||||||
|  |     return TinyGsmIpFromString(getLocalIP()); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Phone Call functions |    * Phone Call functions | ||||||
|    */ |    */ | ||||||
| @@ -573,7 +436,7 @@ public: | |||||||
|   String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE; |   String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Battery functions |    * Battery & temperature functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   // Use: float vBatt = modem.getBattVoltage() / 1000.0; |   // Use: float vBatt = modem.getBattVoltage() / 1000.0; | ||||||
| @@ -582,10 +445,11 @@ public: | |||||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { |     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||||
|       return 0; |       return 0; | ||||||
|     } |     } | ||||||
|     streamSkipUntil(','); // Skip |     streamSkipUntil(','); // Skip battery charge status | ||||||
|     streamSkipUntil(','); // Skip |     streamSkipUntil(','); // Skip battery charge level | ||||||
|  |     // return voltage in mV | ||||||
|     uint16_t res = stream.readStringUntil(',').toInt(); |     uint16_t res = stream.readStringUntil(',').toInt(); | ||||||
|  |     // Wait for final OK | ||||||
|     waitResponse(); |     waitResponse(); | ||||||
|     return res; |     return res; | ||||||
|   } |   } | ||||||
| @@ -595,26 +459,59 @@ public: | |||||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { |     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
|     stream.readStringUntil(','); |     streamSkipUntil(','); // Skip battery charge status | ||||||
|  |     // Read battery charge level | ||||||
|     int res = stream.readStringUntil(',').toInt(); |     int res = stream.readStringUntil(',').toInt(); | ||||||
|  |     // Wait for final OK | ||||||
|     waitResponse(); |     waitResponse(); | ||||||
|     return res; |     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 |    * Client related functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
| protected: | 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; |     int rsp; | ||||||
|  |     uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; | ||||||
|  |  | ||||||
|     // <PDPcontextID>(1-16), <connectID>(0-11),"TCP/UDP/TCP LISTENER/UDP SERVICE", |     // <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) |     // "<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")); |     sendAT(GF("+QIOPEN=1,"), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), port, GF(",0,0")); | ||||||
|     rsp = waitResponse(); |     rsp = waitResponse(); | ||||||
|  |  | ||||||
|     if (waitResponse(20000L, GF(GSM_NL "+QIOPEN:")) != 1) { |     if (waitResponse(timeout_ms, GF(GSM_NL "+QIOPEN:")) != 1) { | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -638,7 +535,6 @@ protected: | |||||||
|       return 0; |       return 0; | ||||||
|     } |     } | ||||||
|     // TODO: Wait for ACK? AT+QISEND=id,0 |     // TODO: Wait for ACK? AT+QISEND=id,0 | ||||||
|     maintain();  // look for a very quick response from the remote |  | ||||||
|     return len; |     return len; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -651,13 +547,10 @@ protected: | |||||||
|     sockets[mux]->sock_available = len; |     sockets[mux]->sock_available = len; | ||||||
|  |  | ||||||
|     for (size_t i=0; i<len; i++) { |     for (size_t i=0; i<len; i++) { | ||||||
|       while (!stream.available()) { TINY_GSM_YIELD(); } |       TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT | ||||||
|       char c = stream.read(); |  | ||||||
|       sockets[mux]->rx.put(c); |  | ||||||
|     } |     } | ||||||
|     waitResponse(); |     waitResponse(); | ||||||
|     DBG("### READ:", len, "from", mux); |     DBG("### READ:", len, "from", mux); | ||||||
|     maintain();  // Listen for a close or other URC |  | ||||||
|     return len; |     return len; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -668,13 +561,12 @@ protected: | |||||||
|       streamSkipUntil(','); // Skip total received |       streamSkipUntil(','); // Skip total received | ||||||
|       streamSkipUntil(','); // Skip have read |       streamSkipUntil(','); // Skip have read | ||||||
|       result = stream.readStringUntil('\n').toInt(); |       result = stream.readStringUntil('\n').toInt(); | ||||||
|       DBG("### DATA AVAILABLE:", result, "on", mux); |       if (result) DBG("### DATA AVAILABLE:", result, "on", mux); | ||||||
|       waitResponse(); |       waitResponse(); | ||||||
|     } |     } | ||||||
|     if (!result) { |     if (!result) { | ||||||
|       sockets[mux]->sock_connected = modemGetConnected(mux); |       sockets[mux]->sock_connected = modemGetConnected(mux); | ||||||
|     } |     } | ||||||
|     maintain();  // Listen for a close or other URC |  | ||||||
|     return result; |     return result; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -693,7 +585,6 @@ protected: | |||||||
|     int res = stream.readStringUntil(',').toInt(); // socket state |     int res = stream.readStringUntil(',').toInt(); // socket state | ||||||
|  |  | ||||||
|     waitResponse(); |     waitResponse(); | ||||||
|     maintain();  // Listen for a close or other URC |  | ||||||
|  |  | ||||||
|     // 0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing |     // 0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing | ||||||
|     return 2 == res; |     return 2 == res; | ||||||
| @@ -705,16 +596,10 @@ public: | |||||||
|    Utilities |    Utilities | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   template<typename... Args> | TINY_GSM_MODEM_STREAM_UTILITIES() | ||||||
|   void sendAT(Args... cmd) { |  | ||||||
|     streamWrite("AT", cmd..., GSM_NL); |  | ||||||
|     stream.flush(); |  | ||||||
|     TINY_GSM_YIELD(); |  | ||||||
|     //DBG("### AT:", cmd...); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // TODO: Optimize this! |   // 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 r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) |                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||||
|   { |   { | ||||||
| @@ -770,7 +655,7 @@ public: | |||||||
|           data = ""; |           data = ""; | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } while (millis() - startMillis < timeout); |     } while (millis() - startMillis < timeout_ms); | ||||||
| finish: | finish: | ||||||
|     if (!index) { |     if (!index) { | ||||||
|       data.trim(); |       data.trim(); | ||||||
| @@ -783,12 +668,12 @@ finish: | |||||||
|     return index; |     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 r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) |                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||||
|   { |   { | ||||||
|     String data; |     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), |   uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||||
|   | |||||||
| @@ -12,6 +12,10 @@ | |||||||
|  |  | ||||||
| //#define TINY_GSM_DEBUG Serial | //#define TINY_GSM_DEBUG Serial | ||||||
|  |  | ||||||
|  | #if !defined(TINY_GSM_RX_BUFFER) | ||||||
|  |   #define TINY_GSM_RX_BUFFER 512 | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #define TINY_GSM_MUX_COUNT 5 | #define TINY_GSM_MUX_COUNT 5 | ||||||
|  |  | ||||||
| #include <TinyGsmCommon.h> | #include <TinyGsmCommon.h> | ||||||
| @@ -36,7 +40,7 @@ enum RegStatus { | |||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class TinyGsmESP8266 : public TinyGsmModem | class TinyGsmESP8266 | ||||||
| { | { | ||||||
|  |  | ||||||
| public: | public: | ||||||
| @@ -59,36 +63,20 @@ public: | |||||||
|     sock_connected = false; |     sock_connected = false; | ||||||
|  |  | ||||||
|     at->sockets[mux] = this; |     at->sockets[mux] = this; | ||||||
|     //  ^^ TODO: attach the socket here at init?  Or later at connect? |  | ||||||
|     // Currently done inconsistently between modems |  | ||||||
|  |  | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| public: | public: | ||||||
|   virtual int connect(const char *host, uint16_t port) { |   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||||
|     stop(); |     stop(); | ||||||
|     TINY_GSM_YIELD(); |     TINY_GSM_YIELD(); | ||||||
|     rx.clear(); |     rx.clear(); | ||||||
|     sock_connected = at->modemConnect(host, port, mux); |     sock_connected = at->modemConnect(host, port, mux, false, timeout_s); | ||||||
|     // 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 |  | ||||||
|     return sock_connected; |     return sock_connected; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   virtual int connect(IPAddress ip, uint16_t port) { | TINY_GSM_CLIENT_CONNECT_OVERLOADS() | ||||||
|     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); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual void stop() { |   virtual void stop() { | ||||||
|     TINY_GSM_YIELD(); |     TINY_GSM_YIELD(); | ||||||
| @@ -98,67 +86,13 @@ public: | |||||||
|     rx.clear(); |     rx.clear(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   virtual size_t write(const uint8_t *buf, size_t size) { | TINY_GSM_CLIENT_WRITE() | ||||||
|     TINY_GSM_YIELD(); |  | ||||||
|     //at->maintain(); |  | ||||||
|     return at->modemSend(buf, size, mux); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual size_t write(uint8_t c) { | TINY_GSM_CLIENT_AVAILABLE_NO_MODEM_FIFO() | ||||||
|     return write(&c, 1); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual size_t write(const char *str) { | TINY_GSM_CLIENT_READ_NO_MODEM_FIFO() | ||||||
|     if (str == NULL) return 0; |  | ||||||
|     return write((const uint8_t *)str, strlen(str)); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual int available() { | TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() | ||||||
|     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(); } |  | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Extended API |    * Extended API | ||||||
| @@ -184,15 +118,11 @@ public: | |||||||
|   {} |   {} | ||||||
|  |  | ||||||
| public: | public: | ||||||
|   virtual int connect(const char *host, uint16_t port) { |   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||||
|     stop(); |     stop(); | ||||||
|     TINY_GSM_YIELD(); |     TINY_GSM_YIELD(); | ||||||
|     rx.clear(); |     rx.clear(); | ||||||
|     sock_connected = at->modemConnect(host, port, mux, true); |     sock_connected = at->modemConnect(host, port, mux, true, timeout_s); | ||||||
|     // 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 |  | ||||||
|     return sock_connected; |     return sock_connected; | ||||||
|   } |   } | ||||||
| }; | }; | ||||||
| @@ -201,7 +131,7 @@ public: | |||||||
| public: | public: | ||||||
|  |  | ||||||
|   TinyGsmESP8266(Stream& stream) |   TinyGsmESP8266(Stream& stream) | ||||||
|     : TinyGsmModem(stream), stream(stream) |     : stream(stream) | ||||||
|   { |   { | ||||||
|     memset(sockets, 0, sizeof(sockets)); |     memset(sockets, 0, sizeof(sockets)); | ||||||
|   } |   } | ||||||
| @@ -210,6 +140,10 @@ public: | |||||||
|    * Basic functions |    * Basic functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|  |   bool begin(const char* pin = NULL) { | ||||||
|  |     return init(pin); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   bool init(const char* pin = NULL) { |   bool init(const char* pin = NULL) { | ||||||
|     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); |     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); | ||||||
|     if (!testAT()) { |     if (!testAT()) { | ||||||
| @@ -239,18 +173,9 @@ public: | |||||||
|     sendAT(GF("+UART_CUR="), baud, "8,1,0,0"); |     sendAT(GF("+UART_CUR="), baud, "8,1,0,0"); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool testAT(unsigned long timeout = 10000L) { | TINY_GSM_MODEM_TEST_AT() | ||||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { |  | ||||||
|       sendAT(GF("")); |  | ||||||
|       if (waitResponse(200) == 1) return true; |  | ||||||
|       delay(100); |  | ||||||
|     } |  | ||||||
|     return false; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   void maintain() { | TINY_GSM_MODEM_MAINTAIN_LISTEN() | ||||||
|     waitResponse(10, NULL, NULL); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool factoryDefault() { |   bool factoryDefault() { | ||||||
|     sendAT(GF("+RESTORE")); |     sendAT(GF("+RESTORE")); | ||||||
| @@ -345,8 +270,8 @@ public: | |||||||
|     return (s == REG_OK_IP || s == REG_OK_TCP); |     return (s == REG_OK_IP || s == REG_OK_TCP); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool waitForNetwork(unsigned long timeout = 60000L) { |   bool waitForNetwork(unsigned long timeout_ms = 60000L) { | ||||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { |     for (unsigned long start = millis(); millis() - start < timeout_ms; ) { | ||||||
|       sendAT(GF("+CIPSTATUS")); |       sendAT(GF("+CIPSTATUS")); | ||||||
|       int res1 = waitResponse(3000, GF("busy p..."), GF("STATUS:")); |       int res1 = waitResponse(3000, GF("busy p..."), GF("STATUS:")); | ||||||
|       if (res1 == 2) { |       if (res1 == 2) { | ||||||
| @@ -364,6 +289,7 @@ public: | |||||||
|   /* |   /* | ||||||
|    * WiFi functions |    * WiFi functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   bool networkConnect(const char* ssid, const char* pwd) { |   bool networkConnect(const char* ssid, const char* pwd) { | ||||||
|     sendAT(GF("+CWJAP_CUR=\""), ssid, GF("\",\""), pwd, GF("\"")); |     sendAT(GF("+CWJAP_CUR=\""), ssid, GF("\",\""), pwd, GF("\"")); | ||||||
|     if (waitResponse(30000L, GFP(GSM_OK), GF(GSM_NL "FAIL" GSM_NL)) != 1) { |     if (waitResponse(30000L, GFP(GSM_OK), GF(GSM_NL "FAIL" GSM_NL)) != 1) { | ||||||
| @@ -395,20 +321,39 @@ public: | |||||||
|     return res2; |     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 |    * Client related functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
| protected: | 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) { |     if (ssl) { | ||||||
|       sendAT(GF("+CIPSSLSIZE=4096")); |       sendAT(GF("+CIPSSLSIZE=4096")); | ||||||
|       waitResponse(); |       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 |     // TODO: Check mux | ||||||
|     int rsp = waitResponse(75000L, |     int rsp = waitResponse(timeout_ms, | ||||||
|                            GFP(GSM_OK), |                            GFP(GSM_OK), | ||||||
|                            GFP(GSM_ERROR), |                            GFP(GSM_ERROR), | ||||||
|                            GF("ALREADY CONNECT")); |                            GF("ALREADY CONNECT")); | ||||||
| @@ -440,16 +385,10 @@ public: | |||||||
|    Utilities |    Utilities | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   template<typename... Args> | TINY_GSM_MODEM_STREAM_UTILITIES() | ||||||
|   void sendAT(Args... cmd) { |  | ||||||
|     streamWrite("AT", cmd..., GSM_NL); |  | ||||||
|     stream.flush(); |  | ||||||
|     TINY_GSM_YIELD(); |  | ||||||
|     //DBG("### AT:", cmd...); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // TODO: Optimize this! |   // 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 r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) |                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||||
|   { |   { | ||||||
| @@ -493,8 +432,7 @@ public: | |||||||
|             DBG("### Got: ", len, "->", sockets[mux]->rx.free()); |             DBG("### Got: ", len, "->", sockets[mux]->rx.free()); | ||||||
|           } |           } | ||||||
|           while (len--) { |           while (len--) { | ||||||
|             while (!stream.available()) { TINY_GSM_YIELD(); } |             TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT | ||||||
|             sockets[mux]->rx.put(stream.read()); |  | ||||||
|           } |           } | ||||||
|           if (len_orig > sockets[mux]->available()) { // TODO |           if (len_orig > sockets[mux]->available()) { // TODO | ||||||
|             DBG("### Fewer characters received than expected: ", sockets[mux]->available(), " vs ", len_orig); |             DBG("### Fewer characters received than expected: ", sockets[mux]->available(), " vs ", len_orig); | ||||||
| @@ -511,7 +449,7 @@ public: | |||||||
|           DBG("### Closed: ", mux); |           DBG("### Closed: ", mux); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } while (millis() - startMillis < timeout); |     } while (millis() - startMillis < timeout_ms); | ||||||
| finish: | finish: | ||||||
|     if (!index) { |     if (!index) { | ||||||
|       data.trim(); |       data.trim(); | ||||||
| @@ -524,12 +462,12 @@ finish: | |||||||
|     return index; |     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 r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) |                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||||
|   { |   { | ||||||
|     String data; |     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), |   uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||||
|   | |||||||
| @@ -12,6 +12,10 @@ | |||||||
|  |  | ||||||
| //#define TINY_GSM_DEBUG Serial | //#define TINY_GSM_DEBUG Serial | ||||||
|  |  | ||||||
|  | #if !defined(TINY_GSM_RX_BUFFER) | ||||||
|  |   #define TINY_GSM_RX_BUFFER 256 | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #define TINY_GSM_MUX_COUNT 2 | #define TINY_GSM_MUX_COUNT 2 | ||||||
|  |  | ||||||
| #include <TinyGsmCommon.h> | #include <TinyGsmCommon.h> | ||||||
| @@ -36,7 +40,7 @@ enum RegStatus { | |||||||
| }; | }; | ||||||
|  |  | ||||||
|  |  | ||||||
| class TinyGsmM590 : public TinyGsmModem | class TinyGsmM590 | ||||||
| { | { | ||||||
|  |  | ||||||
| public: | public: | ||||||
| @@ -59,36 +63,21 @@ public: | |||||||
|     sock_connected = false; |     sock_connected = false; | ||||||
|  |  | ||||||
|     at->sockets[mux] = this; |     at->sockets[mux] = this; | ||||||
|     //  ^^ TODO: attach the socket here at init?  Or later at connect? |  | ||||||
|     // Currently done inconsistently between modems |  | ||||||
|  |  | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| public: | public: | ||||||
|   virtual int connect(const char *host, uint16_t port) { |   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||||
|     stop(); |     stop(); | ||||||
|     TINY_GSM_YIELD(); |     TINY_GSM_YIELD(); | ||||||
|     rx.clear(); |     rx.clear(); | ||||||
|     sock_connected = at->modemConnect(host, port, mux); |     sock_connected = at->modemConnect(host, port, mux, timeout_s); | ||||||
|     // 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 |  | ||||||
|     return sock_connected; |     return sock_connected; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   virtual int connect(IPAddress ip, uint16_t port) { | TINY_GSM_CLIENT_CONNECT_OVERLOADS() | ||||||
|     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); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual void stop() { |   virtual void stop() { | ||||||
|     TINY_GSM_YIELD(); |     TINY_GSM_YIELD(); | ||||||
| @@ -98,67 +87,13 @@ public: | |||||||
|     rx.clear(); |     rx.clear(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   virtual size_t write(const uint8_t *buf, size_t size) { | TINY_GSM_CLIENT_WRITE() | ||||||
|     TINY_GSM_YIELD(); |  | ||||||
|     //at->maintain(); |  | ||||||
|     return at->modemSend(buf, size, mux); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual size_t write(uint8_t c) { | TINY_GSM_CLIENT_AVAILABLE_NO_MODEM_FIFO() | ||||||
|     return write(&c, 1); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual size_t write(const char *str) { | TINY_GSM_CLIENT_READ_NO_MODEM_FIFO() | ||||||
|     if (str == NULL) return 0; |  | ||||||
|     return write((const uint8_t *)str, strlen(str)); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual int available() { | TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() | ||||||
|     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(); } |  | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Extended API |    * Extended API | ||||||
| @@ -167,7 +102,7 @@ public: | |||||||
|   String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; |   String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; | ||||||
|  |  | ||||||
| private: | private: | ||||||
|   TinyGsmM590*   at; |   TinyGsmM590*    at; | ||||||
|   uint8_t         mux; |   uint8_t         mux; | ||||||
|   bool            sock_connected; |   bool            sock_connected; | ||||||
|   RxFifo          rx; |   RxFifo          rx; | ||||||
| @@ -177,7 +112,7 @@ private: | |||||||
| public: | public: | ||||||
|  |  | ||||||
|   TinyGsmM590(Stream& stream) |   TinyGsmM590(Stream& stream) | ||||||
|     : TinyGsmModem(stream), stream(stream) |     : stream(stream) | ||||||
|   { |   { | ||||||
|     memset(sockets, 0, sizeof(sockets)); |     memset(sockets, 0, sizeof(sockets)); | ||||||
|   } |   } | ||||||
| @@ -186,6 +121,10 @@ public: | |||||||
|    * Basic functions |    * Basic functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|  |   bool begin(const char* pin = NULL) { | ||||||
|  |     return init(pin); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   bool init(const char* pin = NULL) { |   bool init(const char* pin = NULL) { | ||||||
|     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); |     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); | ||||||
|     if (!testAT()) { |     if (!testAT()) { | ||||||
| @@ -208,24 +147,11 @@ public: | |||||||
|     return "Neoway M590"; |     return "Neoway M590"; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   void setBaud(unsigned long baud) { | TINY_GSM_MODEM_SET_BAUD_IPR() | ||||||
|     sendAT(GF("+IPR="), baud); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool testAT(unsigned long timeout = 10000L) { | TINY_GSM_MODEM_TEST_AT() | ||||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { |  | ||||||
|       sendAT(GF("")); |  | ||||||
|       if (waitResponse(200) == 1) return true; |  | ||||||
|       delay(100); |  | ||||||
|     } |  | ||||||
|     return false; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   void maintain() { | TINY_GSM_MODEM_MAINTAIN_LISTEN() | ||||||
|     //while (stream.available()) { |  | ||||||
|       waitResponse(10, NULL, NULL); |  | ||||||
|     //} |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool factoryDefault() { |   bool factoryDefault() { | ||||||
|     sendAT(GF("&FZE0&W"));  // Factory + Reset + Echo Off + Write |     sendAT(GF("&FZE0&W"));  // Factory + Reset + Echo Off + Write | ||||||
| @@ -240,17 +166,7 @@ public: | |||||||
|     return waitResponse() == 1; |     return waitResponse() == 1; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   String getModemInfo() { | TINY_GSM_MODEM_GET_INFO_ATI() | ||||||
|     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; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool hasSSL() { |   bool hasSSL() { | ||||||
|     return false; |     return false; | ||||||
| @@ -297,35 +213,14 @@ public: | |||||||
|    * SIM card functions |    * SIM card functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   bool simUnlock(const char *pin) { | TINY_GSM_MODEM_SIM_UNLOCK_CPIN() | ||||||
|     sendAT(GF("+CPIN=\""), pin, GF("\"")); |  | ||||||
|     return waitResponse() == 1; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   String getSimCCID() { | TINY_GSM_MODEM_GET_SIMCCID_CCID() | ||||||
|     sendAT(GF("+CCID")); |  | ||||||
|     if (waitResponse(GF(GSM_NL "+CCID:")) != 1) { |  | ||||||
|       return ""; |  | ||||||
|     } |  | ||||||
|     String res = stream.readStringUntil('\n'); |  | ||||||
|     waitResponse(); |  | ||||||
|     res.trim(); |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   String getIMEI() { | TINY_GSM_MODEM_GET_IMEI_GSN() | ||||||
|     sendAT(GF("+GSN")); |  | ||||||
|     if (waitResponse(GF(GSM_NL)) != 1) { |  | ||||||
|       return ""; |  | ||||||
|     } |  | ||||||
|     String res = stream.readStringUntil('\n'); |  | ||||||
|     waitResponse(); |  | ||||||
|     res.trim(); |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SimStatus getSimStatus(unsigned long timeout = 10000L) { |   SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { | ||||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { |     for (unsigned long start = millis(); millis() - start < timeout_ms; ) { | ||||||
|       sendAT(GF("+CPIN?")); |       sendAT(GF("+CPIN?")); | ||||||
|       if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { |       if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { | ||||||
|         delay(1000); |         delay(1000); | ||||||
| @@ -343,50 +238,27 @@ public: | |||||||
|     return SIM_ERROR; |     return SIM_ERROR; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   RegStatus getRegistrationStatus() { | TINY_GSM_MODEM_GET_REGISTRATION_XREG(CREG) | ||||||
|     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; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   String getOperator() { | TINY_GSM_MODEM_GET_OPERATOR_COPS() | ||||||
|     sendAT(GF("+COPS?")); |  | ||||||
|     if (waitResponse(GF(GSM_NL "+COPS:")) != 1) { |  | ||||||
|       return ""; |  | ||||||
|     } |  | ||||||
|     streamSkipUntil('"'); // Skip mode and format |  | ||||||
|     String res = stream.readStringUntil('"'); |  | ||||||
|     waitResponse(); |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Generic network functions |    * Generic network functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   int16_t getSignalQuality() { | TINY_GSM_MODEM_GET_CSQ() | ||||||
|     sendAT(GF("+CSQ")); |  | ||||||
|     if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) { |  | ||||||
|       return 99; |  | ||||||
|     } |  | ||||||
|     int res = stream.readStringUntil(',').toInt(); |  | ||||||
|     waitResponse(); |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool isNetworkConnected() { |   bool isNetworkConnected() { | ||||||
|     RegStatus s = getRegistrationStatus(); |     RegStatus s = getRegistrationStatus(); | ||||||
|     return (s == REG_OK_HOME || s == REG_OK_ROAMING); |     return (s == REG_OK_HOME || s == REG_OK_ROAMING); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | TINY_GSM_MODEM_WAIT_FOR_NETWORK() | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * GPRS functions |    * GPRS functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { |   bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { | ||||||
|     gprsDisconnect(); |     gprsDisconnect(); | ||||||
|  |  | ||||||
| @@ -404,8 +276,8 @@ public: | |||||||
|     sendAT(GF("+XIIC=1")); |     sendAT(GF("+XIIC=1")); | ||||||
|     waitResponse(); |     waitResponse(); | ||||||
|  |  | ||||||
|     const unsigned long timeout = 60000L; |     const unsigned long timeout_ms = 60000L; | ||||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { |     for (unsigned long start = millis(); millis() - start < timeout_ms; ) { | ||||||
|       if (isGprsConnected()) { |       if (isGprsConnected()) { | ||||||
|         //goto set_dns; // TODO |         //goto set_dns; // TODO | ||||||
|         return true; |         return true; | ||||||
| @@ -456,6 +328,10 @@ public: | |||||||
|     return res; |     return res; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   IPAddress localIP() { | ||||||
|  |     return TinyGsmIpFromString(getLocalIP()); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Phone Call functions |    * Phone Call functions | ||||||
|    */ |    */ | ||||||
| @@ -524,12 +400,14 @@ public: | |||||||
|   String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE; |   String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Battery functions |    * Battery & temperature functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE; |   uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||||
|  |  | ||||||
|   int8_t getBattPercent() 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 |    * Client related functions | ||||||
| @@ -537,12 +415,13 @@ public: | |||||||
|  |  | ||||||
| protected: | 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? |     for (int i=0; i<3; i++) { // TODO: no need for loop? | ||||||
|       String ip = dnsIpQuery(host); |       String ip = dnsIpQuery(host); | ||||||
|  |  | ||||||
|       sendAT(GF("+TCPSETUP="), mux, GF(","), ip, GF(","), port); |       sendAT(GF("+TCPSETUP="), mux, GF(","), ip, GF(","), port); | ||||||
|       int rsp = waitResponse(75000L, |       int rsp = waitResponse(timeout_ms, | ||||||
|                             GF(",OK" GSM_NL), |                             GF(",OK" GSM_NL), | ||||||
|                             GF(",FAIL" GSM_NL), |                             GF(",FAIL" GSM_NL), | ||||||
|                             GF("+TCPSETUP:Error" GSM_NL)); |                             GF("+TCPSETUP:Error" GSM_NL)); | ||||||
| @@ -596,16 +475,10 @@ public: | |||||||
|    Utilities |    Utilities | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   template<typename... Args> | TINY_GSM_MODEM_STREAM_UTILITIES() | ||||||
|   void sendAT(Args... cmd) { |  | ||||||
|     streamWrite("AT", cmd..., GSM_NL); |  | ||||||
|     stream.flush(); |  | ||||||
|     TINY_GSM_YIELD(); |  | ||||||
|     //DBG("### AT:", cmd...); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // TODO: Optimize this! |   // 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 r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) |                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||||
|   { |   { | ||||||
| @@ -649,8 +522,7 @@ public: | |||||||
|             DBG("### Got: ", len, "->", sockets[mux]->rx.free()); |             DBG("### Got: ", len, "->", sockets[mux]->rx.free()); | ||||||
|           } |           } | ||||||
|           while (len--) { |           while (len--) { | ||||||
|             while (!stream.available()) { TINY_GSM_YIELD(); } |             TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT | ||||||
|             sockets[mux]->rx.put(stream.read()); |  | ||||||
|           } |           } | ||||||
|           if (len_orig > sockets[mux]->available()) { // TODO |           if (len_orig > sockets[mux]->available()) { // TODO | ||||||
|             DBG("### Fewer characters received than expected: ", sockets[mux]->available(), " vs ", len_orig); |             DBG("### Fewer characters received than expected: ", sockets[mux]->available(), " vs ", len_orig); | ||||||
| @@ -666,7 +538,7 @@ public: | |||||||
|           DBG("### Closed: ", mux); |           DBG("### Closed: ", mux); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } while (millis() - startMillis < timeout); |     } while (millis() - startMillis < timeout_ms); | ||||||
| finish: | finish: | ||||||
|     if (!index) { |     if (!index) { | ||||||
|       data.trim(); |       data.trim(); | ||||||
| @@ -679,12 +551,12 @@ finish: | |||||||
|     return index; |     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 r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) |                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||||
|   { |   { | ||||||
|     String data; |     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), |   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_DEBUG Serial | ||||||
| //#define TINY_GSM_USE_HEX | //#define TINY_GSM_USE_HEX | ||||||
|  |  | ||||||
|  | #if !defined(TINY_GSM_RX_BUFFER) | ||||||
|  |   #define TINY_GSM_RX_BUFFER 64 | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #define TINY_GSM_MUX_COUNT 6 | #define TINY_GSM_MUX_COUNT 6 | ||||||
|  |  | ||||||
| #include <TinyGsmCommon.h> | #include <TinyGsmCommon.h> | ||||||
| @@ -37,7 +41,7 @@ enum RegStatus { | |||||||
| }; | }; | ||||||
|  |  | ||||||
|  |  | ||||||
| class TinyGsmM95 : public TinyGsmModem | class TinyGsmM95 | ||||||
| { | { | ||||||
|  |  | ||||||
| public: | public: | ||||||
| @@ -62,36 +66,20 @@ public: | |||||||
|     got_data = false; |     got_data = false; | ||||||
|  |  | ||||||
|     at->sockets[mux] = this; |     at->sockets[mux] = this; | ||||||
|     //  ^^ TODO: attach the socket here at init?  Or later at connect? |  | ||||||
|     // Currently done inconsistently between modems |  | ||||||
|  |  | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| public: | public: | ||||||
|   virtual int connect(const char *host, uint16_t port) { |   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||||
|     stop(); |     stop(); | ||||||
|     TINY_GSM_YIELD(); |     TINY_GSM_YIELD(); | ||||||
|     rx.clear(); |     rx.clear(); | ||||||
|     sock_connected = at->modemConnect(host, port, mux); |     sock_connected = at->modemConnect(host, port, mux, false, timeout_s); | ||||||
|     // 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 |  | ||||||
|     return sock_connected; |     return sock_connected; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   virtual int connect(IPAddress ip, uint16_t port) { | TINY_GSM_CLIENT_CONNECT_OVERLOADS() | ||||||
|     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); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual void stop() { |   virtual void stop() { | ||||||
|     TINY_GSM_YIELD(); |     TINY_GSM_YIELD(); | ||||||
| @@ -112,70 +100,13 @@ public: | |||||||
|     at->waitResponse(60000L, GF("CLOSED"), GF("CLOSE OK"), GF("ERROR")); |     at->waitResponse(60000L, GF("CLOSED"), GF("CLOSE OK"), GF("ERROR")); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   virtual size_t write(const uint8_t *buf, size_t size) { | TINY_GSM_CLIENT_WRITE() | ||||||
|     TINY_GSM_YIELD(); |  | ||||||
|     at->maintain(); |  | ||||||
|     return at->modemSend(buf, size, mux); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual size_t write(uint8_t c) { | TINY_GSM_CLIENT_AVAILABLE_NO_BUFFER_CHECK() | ||||||
|     return write(&c, 1); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual size_t write(const char *str) { | TINY_GSM_CLIENT_READ_NO_BUFFER_CHECK() | ||||||
|     if (str == NULL) return 0; |  | ||||||
|     return write((const uint8_t *)str, strlen(str)); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual int available() { | TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() | ||||||
|     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(); } |  | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Extended API |    * Extended API | ||||||
| @@ -203,11 +134,11 @@ private: | |||||||
| //   {} | //   {} | ||||||
| // | // | ||||||
| // public: | // public: | ||||||
| //   virtual int connect(const char *host, uint16_t port) { | //   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||||
| //     stop(); | //     stop(); | ||||||
| //     TINY_GSM_YIELD(); | //     TINY_GSM_YIELD(); | ||||||
| //     rx.clear(); | //     rx.clear(); | ||||||
| //     sock_connected = at->modemConnect(host, port, mux, true); | //     sock_connected = at->modemConnect(host, port, mux, true, timeout_s); | ||||||
| //     return sock_connected; | //     return sock_connected; | ||||||
| //   } | //   } | ||||||
| // }; | // }; | ||||||
| @@ -216,7 +147,7 @@ private: | |||||||
| public: | public: | ||||||
|  |  | ||||||
|   TinyGsmM95(Stream& stream) |   TinyGsmM95(Stream& stream) | ||||||
|     : TinyGsmModem(stream), stream(stream) |     : stream(stream) | ||||||
|   { |   { | ||||||
|     memset(sockets, 0, sizeof(sockets)); |     memset(sockets, 0, sizeof(sockets)); | ||||||
|   } |   } | ||||||
| @@ -225,6 +156,10 @@ public: | |||||||
|    * Basic functions |    * Basic functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|  |   bool begin(const char* pin = NULL) { | ||||||
|  |     return init(pin); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   bool init(const char* pin = NULL) { |   bool init(const char* pin = NULL) { | ||||||
|     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); |     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); | ||||||
|     if (!testAT()) { |     if (!testAT()) { | ||||||
| @@ -247,31 +182,11 @@ public: | |||||||
|     return "Quectel M95"; |     return "Quectel M95"; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   void setBaud(unsigned long baud) { | TINY_GSM_MODEM_SET_BAUD_IPR() | ||||||
|     sendAT(GF("+IPR="), baud); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool testAT(unsigned long timeout = 10000L) { | TINY_GSM_MODEM_TEST_AT() | ||||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { |  | ||||||
|       sendAT(GF("")); |  | ||||||
|       if (waitResponse(200) == 1) return true; |  | ||||||
|       delay(100); |  | ||||||
|     } |  | ||||||
|     return false; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   void maintain() { | TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS() | ||||||
|     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); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool factoryDefault() { |   bool factoryDefault() { | ||||||
|     sendAT(GF("&FZE0&W"));  // Factory + Reset + Echo Off + Write |     sendAT(GF("&FZE0&W"));  // Factory + Reset + Echo Off + Write | ||||||
| @@ -282,17 +197,7 @@ public: | |||||||
|     return waitResponse() == 1; |     return waitResponse() == 1; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   String getModemInfo() { | TINY_GSM_MODEM_GET_INFO_ATI() | ||||||
|     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; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool hasSSL() { |   bool hasSSL() { | ||||||
|     return false;  // TODO: For now |     return false;  // TODO: For now | ||||||
| @@ -345,14 +250,11 @@ public: | |||||||
|    * SIM card functions |    * SIM card functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   bool simUnlock(const char *pin) { | TINY_GSM_MODEM_SIM_UNLOCK_CPIN() | ||||||
|     sendAT(GF("+CPIN=\""), pin, GF("\"")); |  | ||||||
|     return waitResponse() == 1; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   String getSimCCID() { |   String getSimCCID() { | ||||||
|     sendAT(GF("+ICCID")); |     sendAT(GF("+QCCID")); | ||||||
|     if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) { |     if (waitResponse(GF(GSM_NL "+QCCID:")) != 1) { | ||||||
|       return ""; |       return ""; | ||||||
|     } |     } | ||||||
|     String res = stream.readStringUntil('\n'); |     String res = stream.readStringUntil('\n'); | ||||||
| @@ -361,19 +263,10 @@ public: | |||||||
|     return res; |     return res; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   String getIMEI() { | TINY_GSM_MODEM_GET_IMEI_GSN() | ||||||
|     sendAT(GF("+GSN")); |  | ||||||
|     if (waitResponse(GF(GSM_NL)) != 1) { |  | ||||||
|       return ""; |  | ||||||
|     } |  | ||||||
|     String res = stream.readStringUntil('\n'); |  | ||||||
|     waitResponse(); |  | ||||||
|     res.trim(); |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SimStatus getSimStatus(unsigned long timeout = 10000L) { |   SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { | ||||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { |     for (unsigned long start = millis(); millis() - start < timeout_ms; ) { | ||||||
|       sendAT(GF("+CPIN?")); |       sendAT(GF("+CPIN?")); | ||||||
|       if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { |       if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { | ||||||
|         delay(1000); |         delay(1000); | ||||||
| @@ -391,41 +284,15 @@ public: | |||||||
|     return SIM_ERROR; |     return SIM_ERROR; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   RegStatus getRegistrationStatus() { | TINY_GSM_MODEM_GET_REGISTRATION_XREG(CREG) | ||||||
|     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; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   String getOperator() { | TINY_GSM_MODEM_GET_OPERATOR_COPS() | ||||||
|     sendAT(GF("+COPS?")); |  | ||||||
|     if (waitResponse(GF(GSM_NL "+COPS:")) != 1) { |  | ||||||
|       return ""; |  | ||||||
|     } |  | ||||||
|     streamSkipUntil('"'); // Skip mode and format |  | ||||||
|     String res = stream.readStringUntil('"'); |  | ||||||
|     waitResponse(); |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Generic network functions |    * Generic network functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   int16_t getSignalQuality() { | TINY_GSM_MODEM_GET_CSQ() | ||||||
|     sendAT(GF("+CSQ")); |  | ||||||
|     if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) { |  | ||||||
|       return 99; |  | ||||||
|     } |  | ||||||
|     int res = stream.readStringUntil(',').toInt(); |  | ||||||
|     waitResponse(); |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool isNetworkConnected() { |   bool isNetworkConnected() { | ||||||
|     RegStatus s = getRegistrationStatus(); |     RegStatus s = getRegistrationStatus(); | ||||||
| @@ -441,9 +308,12 @@ public: | |||||||
|     waitResponse(); |     waitResponse(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | TINY_GSM_MODEM_WAIT_FOR_NETWORK() | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * GPRS functions |    * GPRS functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { |   bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { | ||||||
|     gprsDisconnect(); |     gprsDisconnect(); | ||||||
|  |  | ||||||
| @@ -497,18 +367,7 @@ public: | |||||||
|     return waitResponse(60000L, GF("DEACT OK"), GF("ERROR")) == 1; |     return waitResponse(60000L, GF("DEACT OK"), GF("ERROR")) == 1; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool isGprsConnected() { | TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() | ||||||
|     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); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * IP Address functions |    * IP Address functions | ||||||
| @@ -522,6 +381,10 @@ public: | |||||||
|     return res; |     return res; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   IPAddress localIP() { | ||||||
|  |     return TinyGsmIpFromString(getLocalIP()); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Messaging functions |    * Messaging functions | ||||||
|    */ |    */ | ||||||
| @@ -621,7 +484,7 @@ public: | |||||||
|   String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE; |   String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Battery functions |    * Battery & temperature functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   // Use: float vBatt = modem.getBattVoltage() / 1000.0; |   // Use: float vBatt = modem.getBattVoltage() / 1000.0; | ||||||
| @@ -630,10 +493,11 @@ public: | |||||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { |     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||||
|       return 0; |       return 0; | ||||||
|     } |     } | ||||||
|     streamSkipUntil(','); // Skip |     streamSkipUntil(','); // Skip battery charge status | ||||||
|     streamSkipUntil(','); // Skip |     streamSkipUntil(','); // Skip battery charge level | ||||||
|  |     // return voltage in mV | ||||||
|     uint16_t res = stream.readStringUntil(',').toInt(); |     uint16_t res = stream.readStringUntil(',').toInt(); | ||||||
|  |     // Wait for final OK | ||||||
|     waitResponse(); |     waitResponse(); | ||||||
|     return res; |     return res; | ||||||
|   } |   } | ||||||
| @@ -643,21 +507,66 @@ public: | |||||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { |     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
|     stream.readStringUntil(','); |     streamSkipUntil(','); // Skip battery charge status | ||||||
|  |     // Read battery charge level | ||||||
|     int res = stream.readStringUntil(',').toInt(); |     int res = stream.readStringUntil(',').toInt(); | ||||||
|  |     // Wait for final OK | ||||||
|     waitResponse(); |     waitResponse(); | ||||||
|     return res; |     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 |    * Client related functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
| protected: | 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); |     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 OK" GSM_NL), | ||||||
|                            GF("CONNECT FAIL" GSM_NL), |                            GF("CONNECT FAIL" GSM_NL), | ||||||
|                            GF("ALREADY CONNECT" GSM_NL)); |                            GF("ALREADY CONNECT" GSM_NL)); | ||||||
| @@ -690,7 +599,6 @@ protected: | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     waitResponse(5000L); |     waitResponse(5000L); | ||||||
|     maintain();  // look for a very quick response from the remote |  | ||||||
|  |  | ||||||
|     // streamSkipUntil(','); // Skip mux |     // streamSkipUntil(','); // Skip mux | ||||||
|     // return stream.readStringUntil('\n').toInt(); |     // return stream.readStringUntil('\n').toInt(); | ||||||
| @@ -706,13 +614,10 @@ protected: | |||||||
|     sockets[mux]->sock_available = len; |     sockets[mux]->sock_available = len; | ||||||
|  |  | ||||||
|     for (size_t i=0; i<len; i++) { |     for (size_t i=0; i<len; i++) { | ||||||
|       while (!stream.available()) { TINY_GSM_YIELD(); } |       TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT | ||||||
|       char c = stream.read(); |  | ||||||
|       sockets[mux]->rx.put(c); |  | ||||||
|     } |     } | ||||||
|     waitResponse(); |     waitResponse(); | ||||||
|     DBG("### READ:", len, "from", mux); |     DBG("### READ:", len, "from", mux); | ||||||
|     maintain();  // Listen for a close or other URC |  | ||||||
|     return len; |     return len; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -723,13 +628,12 @@ protected: | |||||||
|       streamSkipUntil(','); // Skip total received |       streamSkipUntil(','); // Skip total received | ||||||
|       streamSkipUntil(','); // Skip have read |       streamSkipUntil(','); // Skip have read | ||||||
|       result = stream.readStringUntil('\n').toInt(); |       result = stream.readStringUntil('\n').toInt(); | ||||||
|       DBG("### DATA AVAILABLE:", result, "on", mux); |       if (result) DBG("### DATA AVAILABLE:", result, "on", mux); | ||||||
|       waitResponse(); |       waitResponse(); | ||||||
|     } |     } | ||||||
|     if (!result) { |     if (!result) { | ||||||
|       sockets[mux]->sock_connected = modemGetConnected(mux); |       sockets[mux]->sock_connected = modemGetConnected(mux); | ||||||
|     } |     } | ||||||
|     maintain();  // Listen for a close or other URC |  | ||||||
|     return result; |     return result; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -748,7 +652,6 @@ protected: | |||||||
|     int res = stream.readStringUntil(',').toInt(); // socket state |     int res = stream.readStringUntil(',').toInt(); // socket state | ||||||
|  |  | ||||||
|     waitResponse(); |     waitResponse(); | ||||||
|     maintain();  // Listen for a close or other URC |  | ||||||
|  |  | ||||||
|     // 0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing |     // 0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing | ||||||
|     return 2 == res; |     return 2 == res; | ||||||
| @@ -760,16 +663,10 @@ public: | |||||||
|    Utilities |    Utilities | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   template<typename... Args> | TINY_GSM_MODEM_STREAM_UTILITIES() | ||||||
|   void sendAT(Args... cmd) { |  | ||||||
|     streamWrite("AT", cmd..., GSM_NL); |  | ||||||
|     stream.flush(); |  | ||||||
|     TINY_GSM_YIELD(); |  | ||||||
|     //DBG("### AT:", cmd...); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // TODO: Optimize this! |   // 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 r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) |                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||||
|   { |   { | ||||||
| @@ -822,7 +719,7 @@ public: | |||||||
|           DBG("### Closed: ", mux); |           DBG("### Closed: ", mux); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } while (millis() - startMillis < timeout); |     } while (millis() - startMillis < timeout_ms); | ||||||
| finish: | finish: | ||||||
|     if (!index) { |     if (!index) { | ||||||
|       data.trim(); |       data.trim(); | ||||||
| @@ -835,12 +732,12 @@ finish: | |||||||
|     return index; |     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 r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) |                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||||
|   { |   { | ||||||
|     String data; |     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), |   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_DEBUG Serial | ||||||
| //#define TINY_GSM_USE_HEX | //#define TINY_GSM_USE_HEX | ||||||
|  |  | ||||||
|  | #if !defined(TINY_GSM_RX_BUFFER) | ||||||
|  |   #define TINY_GSM_RX_BUFFER 64 | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #define TINY_GSM_MUX_COUNT 6 | #define TINY_GSM_MUX_COUNT 6 | ||||||
|  |  | ||||||
| #include <TinyGsmCommon.h> | #include <TinyGsmCommon.h> | ||||||
| @@ -41,7 +45,7 @@ enum RegStatus { | |||||||
| }; | }; | ||||||
|  |  | ||||||
|  |  | ||||||
| class TinyGsmMC60 : public TinyGsmModem | class TinyGsmMC60 | ||||||
| { | { | ||||||
|  |  | ||||||
| public: | public: | ||||||
| @@ -66,36 +70,20 @@ public: | |||||||
|     got_data = false; |     got_data = false; | ||||||
|  |  | ||||||
|     at->sockets[mux] = this; |     at->sockets[mux] = this; | ||||||
|     //  ^^ TODO: attach the socket here at init?  Or later at connect? |  | ||||||
|     // Currently done inconsistently between modems |  | ||||||
|  |  | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| public: | public: | ||||||
|   virtual int connect(const char *host, uint16_t port) { |   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||||
|     stop(); |     stop(); | ||||||
|     TINY_GSM_YIELD(); |     TINY_GSM_YIELD(); | ||||||
|     rx.clear(); |     rx.clear(); | ||||||
|     sock_connected = at->modemConnect(host, port, mux); |     sock_connected = at->modemConnect(host, port, mux, false, timeout_s); | ||||||
|     // 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 |  | ||||||
|     return sock_connected; |     return sock_connected; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   virtual int connect(IPAddress ip, uint16_t port) { | TINY_GSM_CLIENT_CONNECT_OVERLOADS() | ||||||
|     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); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual void stop() { |   virtual void stop() { | ||||||
|     TINY_GSM_YIELD(); |     TINY_GSM_YIELD(); | ||||||
| @@ -116,70 +104,13 @@ public: | |||||||
|     at->waitResponse(60000L, GF("CLOSED"), GF("CLOSE OK"), GF("ERROR")); |     at->waitResponse(60000L, GF("CLOSED"), GF("CLOSE OK"), GF("ERROR")); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   virtual size_t write(const uint8_t *buf, size_t size) { | TINY_GSM_CLIENT_WRITE() | ||||||
|     TINY_GSM_YIELD(); |  | ||||||
|     at->maintain(); |  | ||||||
|     return at->modemSend(buf, size, mux); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual size_t write(uint8_t c) { | TINY_GSM_CLIENT_AVAILABLE_NO_BUFFER_CHECK() | ||||||
|     return write(&c, 1); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual size_t write(const char *str) { | TINY_GSM_CLIENT_READ_NO_BUFFER_CHECK() | ||||||
|     if (str == NULL) return 0; |  | ||||||
|     return write((const uint8_t *)str, strlen(str)); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual int available() { | TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() | ||||||
|     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(); } |  | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Extended API |    * Extended API | ||||||
| @@ -207,11 +138,11 @@ private: | |||||||
| //   {} | //   {} | ||||||
| // | // | ||||||
| // public: | // public: | ||||||
| //   virtual int connect(const char *host, uint16_t port) { | //   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||||
| //     stop(); | //     stop(); | ||||||
| //     TINY_GSM_YIELD(); | //     TINY_GSM_YIELD(); | ||||||
| //     rx.clear(); | //     rx.clear(); | ||||||
| //     sock_connected = at->modemConnect(host, port, mux, true); | //     sock_connected = at->modemConnect(host, port, mux, true, timeout_s); | ||||||
| //     return sock_connected; | //     return sock_connected; | ||||||
| //   } | //   } | ||||||
| // }; | // }; | ||||||
| @@ -220,7 +151,7 @@ private: | |||||||
| public: | public: | ||||||
|  |  | ||||||
|   TinyGsmMC60(Stream& stream) |   TinyGsmMC60(Stream& stream) | ||||||
|     : TinyGsmModem(stream), stream(stream) |     : stream(stream) | ||||||
|   { |   { | ||||||
|     memset(sockets, 0, sizeof(sockets)); |     memset(sockets, 0, sizeof(sockets)); | ||||||
|   } |   } | ||||||
| @@ -229,6 +160,10 @@ public: | |||||||
|    * Basic functions |    * Basic functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|  |   bool begin(const char* pin = NULL) { | ||||||
|  |     return init(pin); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   bool init(const char* pin = NULL) { |   bool init(const char* pin = NULL) { | ||||||
|     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); |     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); | ||||||
|     if (!testAT()) { |     if (!testAT()) { | ||||||
| @@ -254,31 +189,11 @@ public: | |||||||
|       return "Quectel MC60"; |       return "Quectel MC60"; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   void setBaud(unsigned long baud) { | TINY_GSM_MODEM_SET_BAUD_IPR() | ||||||
|     sendAT(GF("+IPR="), baud); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool testAT(unsigned long timeout = 10000L) { | TINY_GSM_MODEM_TEST_AT() | ||||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { |  | ||||||
|       sendAT(GF("")); |  | ||||||
|       if (waitResponse(200) == 1) return true; |  | ||||||
|       delay(100); |  | ||||||
|     } |  | ||||||
|     return false; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   void maintain() { | TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS() | ||||||
|     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); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool factoryDefault() { |   bool factoryDefault() { | ||||||
|     sendAT(GF("&FZE0&W"));  // Factory + Reset + Echo Off + Write |     sendAT(GF("&FZE0&W"));  // Factory + Reset + Echo Off + Write | ||||||
| @@ -289,17 +204,7 @@ public: | |||||||
|     return waitResponse() == 1; |     return waitResponse() == 1; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   String getModemInfo() { | TINY_GSM_MODEM_GET_INFO_ATI() | ||||||
|     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; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|   * under development |   * under development | ||||||
| @@ -367,35 +272,14 @@ public: | |||||||
|    * SIM card functions |    * SIM card functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   bool simUnlock(const char *pin) { | TINY_GSM_MODEM_SIM_UNLOCK_CPIN() | ||||||
|     sendAT(GF("+CPIN=\""), pin, GF("\"")); |  | ||||||
|     return waitResponse() == 1; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   String getSimCCID() { | TINY_GSM_MODEM_GET_SIMCCID_CCID() | ||||||
|     sendAT(GF("+ICCID")); |  | ||||||
|     if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) { |  | ||||||
|       return ""; |  | ||||||
|     } |  | ||||||
|     String res = stream.readStringUntil('\n'); |  | ||||||
|     waitResponse(); |  | ||||||
|     res.trim(); |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   String getIMEI() { | TINY_GSM_MODEM_GET_IMEI_GSN() | ||||||
|     sendAT(GF("+GSN")); |  | ||||||
|     if (waitResponse(GF(GSM_NL)) != 1) { |  | ||||||
|       return ""; |  | ||||||
|     } |  | ||||||
|     String res = stream.readStringUntil('\n'); |  | ||||||
|     waitResponse(); |  | ||||||
|     res.trim(); |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SimStatus getSimStatus(unsigned long timeout = 10000L) { |   SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { | ||||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { |     for (unsigned long start = millis(); millis() - start < timeout_ms; ) { | ||||||
|       sendAT(GF("+CPIN?")); |       sendAT(GF("+CPIN?")); | ||||||
|       if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { |       if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { | ||||||
|         delay(1000); |         delay(1000); | ||||||
| @@ -415,50 +299,27 @@ public: | |||||||
|     return SIM_ERROR; |     return SIM_ERROR; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   RegStatus getRegistrationStatus() { | TINY_GSM_MODEM_GET_REGISTRATION_XREG(CREG) | ||||||
|     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; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   String getOperator() { | TINY_GSM_MODEM_GET_OPERATOR_COPS() | ||||||
|     sendAT(GF("+COPS?")); |  | ||||||
|     if (waitResponse(GF(GSM_NL "+COPS:")) != 1) { |  | ||||||
|       return ""; |  | ||||||
|     } |  | ||||||
|     streamSkipUntil('"'); // Skip mode and format |  | ||||||
|     String res = stream.readStringUntil('"'); |  | ||||||
|     waitResponse(); |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Generic network functions |    * Generic network functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   int16_t getSignalQuality() { | TINY_GSM_MODEM_GET_CSQ() | ||||||
|     sendAT(GF("+CSQ")); |  | ||||||
|     if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) { |  | ||||||
|       return 99; |  | ||||||
|     } |  | ||||||
|     int res = stream.readStringUntil(',').toInt(); |  | ||||||
|     waitResponse(); |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool isNetworkConnected() { |   bool isNetworkConnected() { | ||||||
|     RegStatus s = getRegistrationStatus(); |     RegStatus s = getRegistrationStatus(); | ||||||
|     return (s == REG_OK_HOME || s == REG_OK_ROAMING); |     return (s == REG_OK_HOME || s == REG_OK_ROAMING); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | TINY_GSM_MODEM_WAIT_FOR_NETWORK() | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * GPRS functions |    * GPRS functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { |   bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { | ||||||
|     gprsDisconnect(); |     gprsDisconnect(); | ||||||
|  |  | ||||||
| @@ -513,7 +374,7 @@ public: | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Check that we have a local IP address |     // Check that we have a local IP address | ||||||
|     if (localIP() != 0) { |     if (localIP() != IPAddress(0,0,0,0)) { | ||||||
|       return true; |       return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -525,18 +386,7 @@ public: | |||||||
|     return waitResponse(60000L, GF("DEACT OK"), GF("ERROR")) == 1; |     return waitResponse(60000L, GF("DEACT OK"), GF("ERROR")) == 1; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool isGprsConnected() { | TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() | ||||||
|     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); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * IP Address functions |    * IP Address functions | ||||||
| @@ -550,6 +400,10 @@ public: | |||||||
|     return res; |     return res; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   IPAddress localIP() { | ||||||
|  |     return TinyGsmIpFromString(getLocalIP()); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Messaging functions |    * Messaging functions | ||||||
|    */ |    */ | ||||||
| @@ -649,7 +503,7 @@ public: | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Battery functions |    * Battery & temperature functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   // Use: float vBatt = modem.getBattVoltage() / 1000.0; |   // Use: float vBatt = modem.getBattVoltage() / 1000.0; | ||||||
| @@ -658,10 +512,11 @@ public: | |||||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { |     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||||
|       return 0; |       return 0; | ||||||
|     } |     } | ||||||
|     streamSkipUntil(','); // Skip |     streamSkipUntil(','); // Skip battery charge status | ||||||
|     streamSkipUntil(','); // Skip |     streamSkipUntil(','); // Skip battery charge level | ||||||
|  |     // return voltage in mV | ||||||
|     uint16_t res = stream.readStringUntil(',').toInt(); |     uint16_t res = stream.readStringUntil(',').toInt(); | ||||||
|  |     // Wait for final OK | ||||||
|     waitResponse(); |     waitResponse(); | ||||||
|     return res; |     return res; | ||||||
|   } |   } | ||||||
| @@ -671,21 +526,53 @@ public: | |||||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { |     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
|     stream.readStringUntil(','); |     streamSkipUntil(','); // Skip battery charge status | ||||||
|  |     // Read battery charge level | ||||||
|     int res = stream.readStringUntil(',').toInt(); |     int res = stream.readStringUntil(',').toInt(); | ||||||
|  |     // Wait for final OK | ||||||
|     waitResponse(); |     waitResponse(); | ||||||
|     return res; |     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 |    * Client related functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
| protected: | 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); |     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 OK" GSM_NL), | ||||||
|                            GF("CONNECT FAIL" GSM_NL), |                            GF("CONNECT FAIL" GSM_NL), | ||||||
|                            GF("ALREADY CONNECT" GSM_NL)); |                            GF("ALREADY CONNECT" GSM_NL)); | ||||||
| @@ -722,7 +609,6 @@ protected: | |||||||
|     // streamSkipUntil(','); // Skip mux |     // streamSkipUntil(','); // Skip mux | ||||||
|     // return stream.readStringUntil('\n').toInt(); |     // return stream.readStringUntil('\n').toInt(); | ||||||
|  |  | ||||||
|     maintain();  // look for a very quick response from the remote |  | ||||||
|     return len;  // TODO |     return len;  // TODO | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -735,13 +621,10 @@ protected: | |||||||
|     sockets[mux]->sock_available = len; |     sockets[mux]->sock_available = len; | ||||||
|  |  | ||||||
|     for (size_t i=0; i<len; i++) { |     for (size_t i=0; i<len; i++) { | ||||||
|       while (!stream.available()) { TINY_GSM_YIELD(); } |       TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT | ||||||
|       char c = stream.read(); |  | ||||||
|       sockets[mux]->rx.put(c); |  | ||||||
|     } |     } | ||||||
|     waitResponse(); |     waitResponse(); | ||||||
|     DBG("### READ:", len, "from", mux); |     DBG("### READ:", len, "from", mux); | ||||||
|     maintain();  // Listen for a close or other URC |  | ||||||
|     return len; |     return len; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -752,13 +635,12 @@ protected: | |||||||
|       streamSkipUntil(','); // Skip total received |       streamSkipUntil(','); // Skip total received | ||||||
|       streamSkipUntil(','); // Skip have read |       streamSkipUntil(','); // Skip have read | ||||||
|       result = stream.readStringUntil('\n').toInt(); |       result = stream.readStringUntil('\n').toInt(); | ||||||
|       DBG("### DATA AVAILABLE:", result, "on", mux); |       if (result) DBG("### DATA AVAILABLE:", result, "on", mux); | ||||||
|       waitResponse(); |       waitResponse(); | ||||||
|     } |     } | ||||||
|     if (!result) { |     if (!result) { | ||||||
|       sockets[mux]->sock_connected = modemGetConnected(mux); |       sockets[mux]->sock_connected = modemGetConnected(mux); | ||||||
|     } |     } | ||||||
|     maintain();  // Listen for a close or other URC |  | ||||||
|     return result; |     return result; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -777,7 +659,6 @@ protected: | |||||||
|     int res = stream.readStringUntil(',').toInt(); // socket state |     int res = stream.readStringUntil(',').toInt(); // socket state | ||||||
|  |  | ||||||
|     waitResponse(); |     waitResponse(); | ||||||
|     maintain();  // Listen for a close or other URC |  | ||||||
|  |  | ||||||
|     // 0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing |     // 0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing | ||||||
|     return 2 == res; |     return 2 == res; | ||||||
| @@ -789,16 +670,10 @@ public: | |||||||
|    Utilities |    Utilities | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   template<typename... Args> | TINY_GSM_MODEM_STREAM_UTILITIES() | ||||||
|   void sendAT(Args... cmd) { |  | ||||||
|     streamWrite("AT", cmd..., GSM_NL); |  | ||||||
|     stream.flush(); |  | ||||||
|     TINY_GSM_YIELD(); |  | ||||||
|     //DBG("### AT:", cmd...); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // TODO: Optimize this! |   // 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 r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL, GsmConstStr r6=NULL) |                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL, GsmConstStr r6=NULL) | ||||||
|   { |   { | ||||||
| @@ -855,7 +730,7 @@ public: | |||||||
|           DBG("### Closed: ", mux); |           DBG("### Closed: ", mux); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } while (millis() - startMillis < timeout); |     } while (millis() - startMillis < timeout_ms); | ||||||
| finish: | finish: | ||||||
|     if (!index) { |     if (!index) { | ||||||
|       data.trim(); |       data.trim(); | ||||||
| @@ -868,12 +743,12 @@ finish: | |||||||
|     return index; |     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 r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL, GsmConstStr r6=NULL) |                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL, GsmConstStr r6=NULL) | ||||||
|   { |   { | ||||||
|     String data; |     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), |   uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ | |||||||
| #ifndef TinyGsmClientSIM7000_h | #ifndef TinyGsmClientSIM7000_h | ||||||
| #define TinyGsmClientSIM7000_h | #define TinyGsmClientSIM7000_h | ||||||
|  |  | ||||||
| #define TINY_GSM_DEBUG Serial | // #define TINY_GSM_DEBUG Serial | ||||||
| //#define TINY_GSM_USE_HEX | //#define TINY_GSM_USE_HEX | ||||||
|  |  | ||||||
| #if !defined(TINY_GSM_RX_BUFFER) | #if !defined(TINY_GSM_RX_BUFFER) | ||||||
| @@ -45,7 +45,7 @@ enum TinyGSMDateTimeFormat { | |||||||
|   DATE_DATE = 2 |   DATE_DATE = 2 | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class TinyGsmSim7000 : public TinyGsmModem | class TinyGsmSim7000 | ||||||
| { | { | ||||||
|  |  | ||||||
| public: | public: | ||||||
| @@ -71,36 +71,20 @@ public: | |||||||
|     got_data = false; |     got_data = false; | ||||||
|  |  | ||||||
|     at->sockets[mux] = this; |     at->sockets[mux] = this; | ||||||
|     //  ^^ TODO: attach the socket here at init?  Or later at connect? |  | ||||||
|     // Currently done inconsistently between modems |  | ||||||
|  |  | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| public: | public: | ||||||
|   virtual int connect(const char *host, uint16_t port) { |   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||||
|     stop(); |     stop(); | ||||||
|     TINY_GSM_YIELD(); |     TINY_GSM_YIELD(); | ||||||
|     rx.clear(); |     rx.clear(); | ||||||
|     sock_connected = at->modemConnect(host, port, mux); |     sock_connected = at->modemConnect(host, port, mux, false, timeout_s); | ||||||
|     // 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 |  | ||||||
|     return sock_connected; |     return sock_connected; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   virtual int connect(IPAddress ip, uint16_t port) { | TINY_GSM_CLIENT_CONNECT_OVERLOADS() | ||||||
|     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); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual void stop() { |   virtual void stop() { | ||||||
|     TINY_GSM_YIELD(); |     TINY_GSM_YIELD(); | ||||||
| @@ -121,86 +105,13 @@ public: | |||||||
|     at->waitResponse(); |     at->waitResponse(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   virtual size_t write(const uint8_t *buf, size_t size) { | TINY_GSM_CLIENT_WRITE() | ||||||
|     TINY_GSM_YIELD(); |  | ||||||
|     at->maintain(); |  | ||||||
|     return at->modemSend(buf, size, mux); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual size_t write(uint8_t c) { | TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK() | ||||||
|     return write(&c, 1); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual size_t write(const char *str) { | TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK() | ||||||
|     if (str == NULL) return 0; |  | ||||||
|     return write((const uint8_t *)str, strlen(str)); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual int available() { | TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() | ||||||
|     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(); } |  | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Extended API |    * Extended API | ||||||
| @@ -229,14 +140,11 @@ public: | |||||||
|   {} |   {} | ||||||
|  |  | ||||||
| public: | public: | ||||||
|   virtual int connect(const char *host, uint16_t port) { |   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||||
|     stop(); |     stop(); | ||||||
|     TINY_GSM_YIELD(); |     TINY_GSM_YIELD(); | ||||||
|     rx.clear(); |     rx.clear(); | ||||||
|     sock_connected = at->modemConnect(host, port, mux, true); |     sock_connected = at->modemConnect(host, port, mux, true, timeout_s); | ||||||
|     // sock_connected = at->modemConnect(host, port, &mux, true); |  | ||||||
|     // at->sockets[mux] = this; |  | ||||||
|     // TODO:  When is the socket attached? |  | ||||||
|     return sock_connected; |     return sock_connected; | ||||||
|   } |   } | ||||||
| }; | }; | ||||||
| @@ -245,7 +153,7 @@ public: | |||||||
| public: | public: | ||||||
|  |  | ||||||
|   TinyGsmSim7000(Stream& stream) |   TinyGsmSim7000(Stream& stream) | ||||||
|     : TinyGsmModem(stream), stream(stream) |     : stream(stream) | ||||||
|   { |   { | ||||||
|     memset(sockets, 0, sizeof(sockets)); |     memset(sockets, 0, sizeof(sockets)); | ||||||
|   } |   } | ||||||
| @@ -254,6 +162,10 @@ public: | |||||||
|    * Basic functions |    * Basic functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|  |   bool begin(const char* pin = NULL) { | ||||||
|  |     return init(pin); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   bool init(const char* pin = NULL) { |   bool init(const char* pin = NULL) { | ||||||
|     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); |     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); | ||||||
|     if (!testAT()) { |     if (!testAT()) { | ||||||
| @@ -272,48 +184,17 @@ public: | |||||||
|     return "SIMCom SIM7000"; |     return "SIMCom SIM7000"; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   void setBaud(unsigned long baud) { | TINY_GSM_MODEM_SET_BAUD_IPR() | ||||||
|     sendAT(GF("+IPR="), baud); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool testAT(unsigned long timeout = 10000L) { | TINY_GSM_MODEM_TEST_AT() | ||||||
|     //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; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   void maintain() { | TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS() | ||||||
|     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); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool factoryDefault() {  // these commands aren't supported |   bool factoryDefault() {  // these commands aren't supported | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   String getModemInfo() { | TINY_GSM_MODEM_GET_INFO_ATI() | ||||||
|     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; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool hasSSL() { |   bool hasSSL() { | ||||||
|     return false;  // TODO:  Module supports SSL, but not yet implemented |     return false;  // TODO:  Module supports SSL, but not yet implemented | ||||||
| @@ -382,35 +263,14 @@ public: | |||||||
|    * SIM card functions |    * SIM card functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   bool simUnlock(const char *pin) { | TINY_GSM_MODEM_SIM_UNLOCK_CPIN() | ||||||
|     sendAT(GF("+CPIN=\""), pin, GF("\"")); |  | ||||||
|     return waitResponse() == 1; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   String getSimCCID() { | TINY_GSM_MODEM_GET_SIMCCID_CCID() | ||||||
|     sendAT(GF("+ICCID")); |  | ||||||
|     if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) { |  | ||||||
|       return ""; |  | ||||||
|     } |  | ||||||
|     String res = stream.readStringUntil('\n'); |  | ||||||
|     waitResponse(); |  | ||||||
|     res.trim(); |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   String getIMEI() { | TINY_GSM_MODEM_GET_IMEI_GSN() | ||||||
|     sendAT(GF("+GSN")); |  | ||||||
|     if (waitResponse(GF(GSM_NL)) != 1) { |  | ||||||
|       return ""; |  | ||||||
|     } |  | ||||||
|     String res = stream.readStringUntil('\n'); |  | ||||||
|     waitResponse(); |  | ||||||
|     res.trim(); |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SimStatus getSimStatus(unsigned long timeout = 10000L) { |   SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { | ||||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { |     for (unsigned long start = millis(); millis() - start < timeout_ms; ) { | ||||||
|       sendAT(GF("+CPIN?")); |       sendAT(GF("+CPIN?")); | ||||||
|       if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { |       if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { | ||||||
|         delay(1000); |         delay(1000); | ||||||
| @@ -428,42 +288,15 @@ public: | |||||||
|     return SIM_ERROR; |     return SIM_ERROR; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   RegStatus getRegistrationStatus() { | TINY_GSM_MODEM_GET_REGISTRATION_XREG(CGREG) | ||||||
|     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; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   String getOperator() { | TINY_GSM_MODEM_GET_OPERATOR_COPS() | ||||||
|     sendAT(GF("+COPS?")); |  | ||||||
|     if (waitResponse(GF(GSM_NL "+COPS:")) != 1) { |  | ||||||
|       return ""; |  | ||||||
|     } |  | ||||||
|     streamSkipUntil('"'); // Skip mode and format |  | ||||||
|     String res = stream.readStringUntil('"'); |  | ||||||
|     waitResponse(); |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Generic network functions |    * Generic network functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   int16_t getSignalQuality() { | TINY_GSM_MODEM_GET_CSQ() | ||||||
|     sendAT(GF("+CSQ")); |  | ||||||
|     if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) { |  | ||||||
|       return 99; |  | ||||||
|     } |  | ||||||
|     int res = stream.readStringUntil(',').toInt(); |  | ||||||
|     waitResponse(); |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool isNetworkConnected() { |   bool isNetworkConnected() { | ||||||
|     RegStatus s = getRegistrationStatus(); |     RegStatus s = getRegistrationStatus(); | ||||||
| @@ -480,6 +313,8 @@ public: | |||||||
|     return res; |     return res; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | TINY_GSM_MODEM_WAIT_FOR_NETWORK() | ||||||
|  |  | ||||||
|   String setNetworkMode(uint8_t mode) { |   String setNetworkMode(uint8_t mode) { | ||||||
|       sendAT(GF("+CNMP="), mode); |       sendAT(GF("+CNMP="), mode); | ||||||
|       if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { |       if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { | ||||||
| @@ -514,6 +349,7 @@ public: | |||||||
|   /* |   /* | ||||||
|    * GPRS functions |    * GPRS functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { |   bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { | ||||||
|     gprsDisconnect(); |     gprsDisconnect(); | ||||||
|  |  | ||||||
| @@ -641,6 +477,10 @@ public: | |||||||
|     return res; |     return res; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   IPAddress localIP() { | ||||||
|  |     return TinyGsmIpFromString(getLocalIP()); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Phone Call functions |    * Phone Call functions | ||||||
| @@ -814,6 +654,7 @@ public: | |||||||
|   /* |   /* | ||||||
|    * Time functions |    * Time functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   String getGSMDateTime(TinyGSMDateTimeFormat format) { |   String getGSMDateTime(TinyGSMDateTimeFormat format) { | ||||||
|     sendAT(GF("+CCLK?")); |     sendAT(GF("+CCLK?")); | ||||||
|     if (waitResponse(2000L, GF(GSM_NL "+CCLK: \"")) != 1) { |     if (waitResponse(2000L, GF(GSM_NL "+CCLK: \"")) != 1) { | ||||||
| @@ -887,15 +728,16 @@ public: | |||||||
|   /* |   /* | ||||||
|    * Battery functions |    * Battery functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   // Use: float vBatt = modem.getBattVoltage() / 1000.0; |   // Use: float vBatt = modem.getBattVoltage() / 1000.0; | ||||||
|   uint16_t getBattVoltage() { |   uint16_t getBattVoltage() { | ||||||
|     sendAT(GF("+CBC")); |     sendAT(GF("+CBC")); | ||||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { |     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||||
|       return 0; |       return 0; | ||||||
|     } |     } | ||||||
|     streamSkipUntil(','); // Skip |     streamSkipUntil(','); // Skip battery charge status | ||||||
|     streamSkipUntil(','); // Skip |     streamSkipUntil(','); // Skip battery charge level | ||||||
|  |     // return voltage in mV | ||||||
|     uint16_t res = stream.readStringUntil(',').toInt(); |     uint16_t res = stream.readStringUntil(',').toInt(); | ||||||
|     waitResponse(); |     waitResponse(); | ||||||
|     return res; |     return res; | ||||||
| @@ -906,22 +748,53 @@ public: | |||||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { |     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
|     stream.readStringUntil(','); |     streamSkipUntil(','); // Skip battery charge status | ||||||
|  |     // Read battery charge level | ||||||
|     int res = stream.readStringUntil(',').toInt(); |     int res = stream.readStringUntil(',').toInt(); | ||||||
|     waitResponse(); |     waitResponse(); | ||||||
|     return res; |     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 |    * Client related functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
| protected: | 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; |     int rsp; | ||||||
|  |     uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; | ||||||
|     sendAT(GF("+CIPSTART="), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), port); |     sendAT(GF("+CIPSTART="), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), port); | ||||||
|     rsp = waitResponse(75000L, |     rsp = waitResponse(timeout_ms, | ||||||
|                        GF("CONNECT OK" GSM_NL), |                        GF("CONNECT OK" GSM_NL), | ||||||
|                        GF("CONNECT FAIL" GSM_NL), |                        GF("CONNECT FAIL" GSM_NL), | ||||||
|                        GF("ALREADY CONNECT" GSM_NL), |                        GF("ALREADY CONNECT" GSM_NL), | ||||||
| @@ -942,7 +815,6 @@ protected: | |||||||
|       return 0; |       return 0; | ||||||
|     } |     } | ||||||
|     streamSkipUntil(','); // Skip mux |     streamSkipUntil(','); // Skip mux | ||||||
|     maintain();  // look for a very quick response from the remote |  | ||||||
|     return stream.readStringUntil('\n').toInt(); |     return stream.readStringUntil('\n').toInt(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -963,30 +835,30 @@ protected: | |||||||
|     size_t len_requested = stream.readStringUntil(',').toInt(); |     size_t len_requested = stream.readStringUntil(',').toInt(); | ||||||
|     //  ^^ Requested number of data bytes (1-1460 bytes)to be read |     //  ^^ Requested number of data bytes (1-1460 bytes)to be read | ||||||
|     size_t len_confirmed = stream.readStringUntil('\n').toInt(); |     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!"); |       DBG(len_requested - len_confirmed, "fewer bytes confirmed than requested!"); | ||||||
|     } |     } | ||||||
|     sockets[mux]->sock_available = len_confirmed; |     sockets[mux]->sock_available = len_confirmed; | ||||||
|     // ^^ Confirmed number of data bytes to be read, which may be less than requested. |     // ^^ Confirmed number of data bytes to be read, which may be less than requested. | ||||||
|     // 0 indicates that no data can be read. |     // 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 | #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, }; |       char buf[4] = { 0, }; | ||||||
|       buf[0] = stream.read(); |       buf[0] = stream.read(); | ||||||
|       buf[1] = stream.read(); |       buf[1] = stream.read(); | ||||||
|       char c = strtol(buf, NULL, 16); |       char c = strtol(buf, NULL, 16); | ||||||
| #else | #else | ||||||
|       while (!stream.available()) { TINY_GSM_YIELD(); } |       while (!stream.available() && (millis() - startMillis < sockets[mux]->_timeout)) { TINY_GSM_YIELD(); } | ||||||
|       char c = stream.read(); |       char c = stream.read(); | ||||||
| #endif | #endif | ||||||
|       sockets[mux]->rx.put(c); |       sockets[mux]->rx.put(c); | ||||||
|     } |     } | ||||||
|     waitResponse(); |     waitResponse(); | ||||||
|     DBG("### READ:", len_confirmed, "from", mux); |     DBG("### READ:", TinyGsmMin(len_confirmed, len_requested), "from", mux); | ||||||
|     maintain();  // Listen for a close or other URC |     return TinyGsmMin(len_confirmed, len_requested); | ||||||
|     return len_confirmed; |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   size_t modemGetAvailable(uint8_t mux) { |   size_t modemGetAvailable(uint8_t mux) { | ||||||
| @@ -1001,7 +873,6 @@ protected: | |||||||
|     if (!result) { |     if (!result) { | ||||||
|       sockets[mux]->sock_connected = modemGetConnected(mux); |       sockets[mux]->sock_connected = modemGetConnected(mux); | ||||||
|     } |     } | ||||||
|     maintain();  // Listen for a close or other URC |  | ||||||
|     return result; |     return result; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -1009,7 +880,6 @@ protected: | |||||||
|     sendAT(GF("+CIPSTATUS="), mux); |     sendAT(GF("+CIPSTATUS="), mux); | ||||||
|     int res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""), GF(",\"CLOSING\""), GF(",\"INITIAL\"")); |     int res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""), GF(",\"CLOSING\""), GF(",\"INITIAL\"")); | ||||||
|     waitResponse(); |     waitResponse(); | ||||||
|     maintain();  // Listen for a close or other URC |  | ||||||
|     return 1 == res; |     return 1 == res; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -1019,16 +889,10 @@ public: | |||||||
|    Utilities |    Utilities | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   template<typename... Args> | TINY_GSM_MODEM_STREAM_UTILITIES() | ||||||
|   void sendAT(Args... cmd) { |  | ||||||
|     streamWrite("AT", cmd..., GSM_NL); |  | ||||||
|     stream.flush(); |  | ||||||
|     TINY_GSM_YIELD(); |  | ||||||
|     //DBG("### AT:", cmd...); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // TODO: Optimize this! |   // 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 r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) |                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||||
|   { |   { | ||||||
| @@ -1094,7 +958,7 @@ public: | |||||||
|           DBG("### Closed: ", mux); |           DBG("### Closed: ", mux); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } while (millis() - startMillis < timeout); |     } while (millis() - startMillis < timeout_ms); | ||||||
| finish: | finish: | ||||||
|     if (!index) { |     if (!index) { | ||||||
|       data.trim(); |       data.trim(); | ||||||
| @@ -1107,12 +971,12 @@ finish: | |||||||
|     return index; |     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 r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) |                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||||
|   { |   { | ||||||
|     String data; |     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), |   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_DEBUG Serial | ||||||
| //#define TINY_GSM_USE_HEX | //#define TINY_GSM_USE_HEX | ||||||
|  |  | ||||||
|  | #if !defined(TINY_GSM_RX_BUFFER) | ||||||
|  |   #define TINY_GSM_RX_BUFFER 64 | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #define TINY_GSM_MUX_COUNT 5 | #define TINY_GSM_MUX_COUNT 5 | ||||||
|  |  | ||||||
| #include <TinyGsmCommon.h> | #include <TinyGsmCommon.h> | ||||||
| @@ -42,7 +46,7 @@ enum TinyGSMDateTimeFormat { | |||||||
|   DATE_DATE = 2 |   DATE_DATE = 2 | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class TinyGsmSim800 : public TinyGsmModem | class TinyGsmSim800 | ||||||
| { | { | ||||||
|  |  | ||||||
| public: | public: | ||||||
| @@ -68,36 +72,20 @@ public: | |||||||
|     got_data = false; |     got_data = false; | ||||||
|  |  | ||||||
|     at->sockets[mux] = this; |     at->sockets[mux] = this; | ||||||
|     //  ^^ TODO: attach the socket here at init?  Or later at connect? |  | ||||||
|     // Currently done inconsistently between modems |  | ||||||
|  |  | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| public: | public: | ||||||
|   virtual int connect(const char *host, uint16_t port) { |   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||||
|     stop(); |     stop(); | ||||||
|     TINY_GSM_YIELD(); |     TINY_GSM_YIELD(); | ||||||
|     rx.clear(); |     rx.clear(); | ||||||
|     sock_connected = at->modemConnect(host, port, mux); |     sock_connected = at->modemConnect(host, port, mux, false, timeout_s); | ||||||
|     // 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 |  | ||||||
|     return sock_connected; |     return sock_connected; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   virtual int connect(IPAddress ip, uint16_t port) { | TINY_GSM_CLIENT_CONNECT_OVERLOADS() | ||||||
|     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); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual void stop() { |   virtual void stop() { | ||||||
|     TINY_GSM_YIELD(); |     TINY_GSM_YIELD(); | ||||||
| @@ -118,84 +106,13 @@ public: | |||||||
|     at->waitResponse(); |     at->waitResponse(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   virtual size_t write(const uint8_t *buf, size_t size) { | TINY_GSM_CLIENT_WRITE() | ||||||
|     TINY_GSM_YIELD(); |  | ||||||
|     at->maintain(); |  | ||||||
|     return at->modemSend(buf, size, mux); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual size_t write(uint8_t c) { | TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK() | ||||||
|     return write(&c, 1); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual size_t write(const char *str) { | TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK() | ||||||
|     if (str == NULL) return 0; |  | ||||||
|     return write((const uint8_t *)str, strlen(str)); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual int available() { | TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() | ||||||
|     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(); } |  | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Extended API |    * Extended API | ||||||
| @@ -224,14 +141,11 @@ public: | |||||||
|   {} |   {} | ||||||
|  |  | ||||||
| public: | public: | ||||||
|   virtual int connect(const char *host, uint16_t port) { |   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||||
|     stop(); |     stop(); | ||||||
|     TINY_GSM_YIELD(); |     TINY_GSM_YIELD(); | ||||||
|     rx.clear(); |     rx.clear(); | ||||||
|     sock_connected = at->modemConnect(host, port, mux, true); |     sock_connected = at->modemConnect(host, port, mux, true, timeout_s); | ||||||
|     // sock_connected = at->modemConnect(host, port, &mux, true); |  | ||||||
|     // at->sockets[mux] = this; |  | ||||||
|     // TODO:  When is the socket attached? |  | ||||||
|     return sock_connected; |     return sock_connected; | ||||||
|   } |   } | ||||||
| }; | }; | ||||||
| @@ -240,7 +154,7 @@ public: | |||||||
| public: | public: | ||||||
|  |  | ||||||
|   TinyGsmSim800(Stream& stream) |   TinyGsmSim800(Stream& stream) | ||||||
|     : TinyGsmModem(stream), stream(stream) |     : stream(stream) | ||||||
|   { |   { | ||||||
|     memset(sockets, 0, sizeof(sockets)); |     memset(sockets, 0, sizeof(sockets)); | ||||||
|   } |   } | ||||||
| @@ -249,6 +163,10 @@ public: | |||||||
|    * Basic functions |    * Basic functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|  |   bool begin(const char* pin = NULL) { | ||||||
|  |     return init(pin); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   bool init(const char* pin = NULL) { |   bool init(const char* pin = NULL) { | ||||||
|     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); |     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); | ||||||
|     if (!testAT()) { |     if (!testAT()) { | ||||||
| @@ -278,32 +196,11 @@ public: | |||||||
|     return "SIMCom SIM800"; |     return "SIMCom SIM800"; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   void setBaud(unsigned long baud) { | TINY_GSM_MODEM_SET_BAUD_IPR() | ||||||
|     sendAT(GF("+IPR="), baud); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool testAT(unsigned long timeout = 10000L) { | TINY_GSM_MODEM_TEST_AT() | ||||||
|     //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; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   void maintain() { | TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS() | ||||||
|     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); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool factoryDefault() { |   bool factoryDefault() { | ||||||
|     sendAT(GF("&FZE0&W"));  // Factory + Reset + Echo Off + Write |     sendAT(GF("&FZE0&W"));  // Factory + Reset + Echo Off + Write | ||||||
| @@ -320,17 +217,7 @@ public: | |||||||
|     return waitResponse() == 1; |     return waitResponse() == 1; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   String getModemInfo() { | TINY_GSM_MODEM_GET_INFO_ATI() | ||||||
|     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; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool hasSSL() { |   bool hasSSL() { | ||||||
| #if defined(TINY_GSM_MODEM_SIM900) | #if defined(TINY_GSM_MODEM_SIM900) | ||||||
| @@ -408,35 +295,14 @@ public: | |||||||
|    * SIM card functions |    * SIM card functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   bool simUnlock(const char *pin) { | TINY_GSM_MODEM_SIM_UNLOCK_CPIN() | ||||||
|     sendAT(GF("+CPIN=\""), pin, GF("\"")); |  | ||||||
|     return waitResponse() == 1; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   String getSimCCID() { | TINY_GSM_MODEM_GET_SIMCCID_CCID() | ||||||
|     sendAT(GF("+ICCID")); |  | ||||||
|     if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) { |  | ||||||
|       return ""; |  | ||||||
|     } |  | ||||||
|     String res = stream.readStringUntil('\n'); |  | ||||||
|     waitResponse(); |  | ||||||
|     res.trim(); |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   String getIMEI() { | TINY_GSM_MODEM_GET_IMEI_GSN() | ||||||
|     sendAT(GF("+GSN")); |  | ||||||
|     if (waitResponse(GF(GSM_NL)) != 1) { |  | ||||||
|       return ""; |  | ||||||
|     } |  | ||||||
|     String res = stream.readStringUntil('\n'); |  | ||||||
|     waitResponse(); |  | ||||||
|     res.trim(); |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SimStatus getSimStatus(unsigned long timeout = 10000L) { |   SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { | ||||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { |     for (unsigned long start = millis(); millis() - start < timeout_ms; ) { | ||||||
|       sendAT(GF("+CPIN?")); |       sendAT(GF("+CPIN?")); | ||||||
|       if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { |       if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { | ||||||
|         delay(1000); |         delay(1000); | ||||||
| @@ -454,50 +320,27 @@ public: | |||||||
|     return SIM_ERROR; |     return SIM_ERROR; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   RegStatus getRegistrationStatus() { | TINY_GSM_MODEM_GET_REGISTRATION_XREG(CREG) | ||||||
|     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; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   String getOperator() { | TINY_GSM_MODEM_GET_OPERATOR_COPS() | ||||||
|     sendAT(GF("+COPS?")); |  | ||||||
|     if (waitResponse(GF(GSM_NL "+COPS:")) != 1) { |  | ||||||
|       return ""; |  | ||||||
|     } |  | ||||||
|     streamSkipUntil('"'); // Skip mode and format |  | ||||||
|     String res = stream.readStringUntil('"'); |  | ||||||
|     waitResponse(); |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Generic network functions |    * Generic network functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   int16_t getSignalQuality() { | TINY_GSM_MODEM_GET_CSQ() | ||||||
|     sendAT(GF("+CSQ")); |  | ||||||
|     if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) { |  | ||||||
|       return 99; |  | ||||||
|     } |  | ||||||
|     int res = stream.readStringUntil(',').toInt(); |  | ||||||
|     waitResponse(); |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool isNetworkConnected() { |   bool isNetworkConnected() { | ||||||
|     RegStatus s = getRegistrationStatus(); |     RegStatus s = getRegistrationStatus(); | ||||||
|     return (s == REG_OK_HOME || s == REG_OK_ROAMING); |     return (s == REG_OK_HOME || s == REG_OK_ROAMING); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | TINY_GSM_MODEM_WAIT_FOR_NETWORK() | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * GPRS functions |    * GPRS functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { |   bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { | ||||||
|     gprsDisconnect(); |     gprsDisconnect(); | ||||||
|  |  | ||||||
| @@ -616,6 +459,7 @@ public: | |||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * IP Address functions |    * IP Address functions | ||||||
|    */ |    */ | ||||||
| @@ -632,6 +476,9 @@ public: | |||||||
|     return res; |     return res; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   IPAddress localIP() { | ||||||
|  |     return TinyGsmIpFromString(getLocalIP()); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Phone Call functions |    * Phone Call functions | ||||||
| @@ -775,6 +622,7 @@ public: | |||||||
|   /* |   /* | ||||||
|    * Time functions |    * Time functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   String getGSMDateTime(TinyGSMDateTimeFormat format) { |   String getGSMDateTime(TinyGSMDateTimeFormat format) { | ||||||
|     sendAT(GF("+CCLK?")); |     sendAT(GF("+CCLK?")); | ||||||
|     if (waitResponse(2000L, GF(GSM_NL "+CCLK: \"")) != 1) { |     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; |   // Use: float vBatt = modem.getBattVoltage() / 1000.0; | ||||||
| @@ -808,10 +656,11 @@ public: | |||||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { |     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||||
|       return 0; |       return 0; | ||||||
|     } |     } | ||||||
|     streamSkipUntil(','); // Skip |     streamSkipUntil(','); // Skip battery charge status | ||||||
|     streamSkipUntil(','); // Skip |     streamSkipUntil(','); // Skip battery charge level | ||||||
|  |     // return voltage in mV | ||||||
|     uint16_t res = stream.readStringUntil(',').toInt(); |     uint16_t res = stream.readStringUntil(',').toInt(); | ||||||
|  |     // Wait for final OK | ||||||
|     waitResponse(); |     waitResponse(); | ||||||
|     return res; |     return res; | ||||||
|   } |   } | ||||||
| @@ -821,20 +670,52 @@ public: | |||||||
|     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { |     if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
|     stream.readStringUntil(','); |     streamSkipUntil(','); // Skip battery charge status | ||||||
|  |     // Read battery charge level | ||||||
|     int res = stream.readStringUntil(',').toInt(); |     int res = stream.readStringUntil(',').toInt(); | ||||||
|  |     // Wait for final OK | ||||||
|     waitResponse(); |     waitResponse(); | ||||||
|     return res; |     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 |    * Client related functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
| protected: | 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; |     int rsp; | ||||||
|  |     uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; | ||||||
| #if !defined(TINY_GSM_MODEM_SIM900) | #if !defined(TINY_GSM_MODEM_SIM900) | ||||||
|     sendAT(GF("+CIPSSL="), ssl); |     sendAT(GF("+CIPSSL="), ssl); | ||||||
|     rsp = waitResponse(); |     rsp = waitResponse(); | ||||||
| @@ -843,7 +724,7 @@ protected: | |||||||
|     } |     } | ||||||
| #endif | #endif | ||||||
|     sendAT(GF("+CIPSTART="), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), port); |     sendAT(GF("+CIPSTART="), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), port); | ||||||
|     rsp = waitResponse(75000L, |     rsp = waitResponse(timeout_ms, | ||||||
|                        GF("CONNECT OK" GSM_NL), |                        GF("CONNECT OK" GSM_NL), | ||||||
|                        GF("CONNECT FAIL" GSM_NL), |                        GF("CONNECT FAIL" GSM_NL), | ||||||
|                        GF("ALREADY CONNECT" GSM_NL), |                        GF("ALREADY CONNECT" GSM_NL), | ||||||
| @@ -864,7 +745,6 @@ protected: | |||||||
|       return 0; |       return 0; | ||||||
|     } |     } | ||||||
|     streamSkipUntil(','); // Skip mux |     streamSkipUntil(','); // Skip mux | ||||||
|     maintain();  // look for a very quick response from the remote |  | ||||||
|     return stream.readStringUntil('\n').toInt(); |     return stream.readStringUntil('\n').toInt(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -885,30 +765,30 @@ protected: | |||||||
|     size_t len_requested = stream.readStringUntil(',').toInt(); |     size_t len_requested = stream.readStringUntil(',').toInt(); | ||||||
|     //  ^^ Requested number of data bytes (1-1460 bytes)to be read |     //  ^^ Requested number of data bytes (1-1460 bytes)to be read | ||||||
|     size_t len_confirmed = stream.readStringUntil('\n').toInt(); |     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!"); |       DBG(len_requested - len_confirmed, "fewer bytes confirmed than requested!"); | ||||||
|     } |     } | ||||||
|     sockets[mux]->sock_available = len_confirmed; |     sockets[mux]->sock_available = len_confirmed; | ||||||
|     // ^^ Confirmed number of data bytes to be read, which may be less than requested. |     // ^^ Confirmed number of data bytes to be read, which may be less than requested. | ||||||
|     // 0 indicates that no data can be read. |     // 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 | #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, }; |       char buf[4] = { 0, }; | ||||||
|       buf[0] = stream.read(); |       buf[0] = stream.read(); | ||||||
|       buf[1] = stream.read(); |       buf[1] = stream.read(); | ||||||
|       char c = strtol(buf, NULL, 16); |       char c = strtol(buf, NULL, 16); | ||||||
| #else | #else | ||||||
|       while (!stream.available()) { TINY_GSM_YIELD(); } |       while (!stream.available() && (millis() - startMillis < sockets[mux]->_timeout)) { TINY_GSM_YIELD(); } | ||||||
|       char c = stream.read(); |       char c = stream.read(); | ||||||
| #endif | #endif | ||||||
|       sockets[mux]->rx.put(c); |       sockets[mux]->rx.put(c); | ||||||
|     } |     } | ||||||
|     waitResponse(); |     waitResponse(); | ||||||
|     DBG("### READ:", len_confirmed, "from", mux); |     DBG("### READ:", TinyGsmMin(len_confirmed, len_requested), "from", mux); | ||||||
|     maintain();  // Listen for a close or other URC |     return TinyGsmMin(len_confirmed, len_requested); | ||||||
|     return len_confirmed; |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   size_t modemGetAvailable(uint8_t mux) { |   size_t modemGetAvailable(uint8_t mux) { | ||||||
| @@ -923,7 +803,6 @@ protected: | |||||||
|     if (!result) { |     if (!result) { | ||||||
|       sockets[mux]->sock_connected = modemGetConnected(mux); |       sockets[mux]->sock_connected = modemGetConnected(mux); | ||||||
|     } |     } | ||||||
|     maintain();  // Listen for a close or other URC |  | ||||||
|     return result; |     return result; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -931,7 +810,6 @@ protected: | |||||||
|     sendAT(GF("+CIPSTATUS="), mux); |     sendAT(GF("+CIPSTATUS="), mux); | ||||||
|     int res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""), GF(",\"CLOSING\""), GF(",\"INITIAL\"")); |     int res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""), GF(",\"CLOSING\""), GF(",\"INITIAL\"")); | ||||||
|     waitResponse(); |     waitResponse(); | ||||||
|     maintain();  // Listen for a close or other URC |  | ||||||
|     return 1 == res; |     return 1 == res; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -941,16 +819,10 @@ public: | |||||||
|    Utilities |    Utilities | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   template<typename... Args> | TINY_GSM_MODEM_STREAM_UTILITIES() | ||||||
|   void sendAT(Args... cmd) { |  | ||||||
|     streamWrite("AT", cmd..., GSM_NL); |  | ||||||
|     stream.flush(); |  | ||||||
|     TINY_GSM_YIELD(); |  | ||||||
|     //DBG("### AT:", cmd...); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // TODO: Optimize this! |   // 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 r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) |                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||||
|   { |   { | ||||||
| @@ -1016,7 +888,7 @@ public: | |||||||
|           DBG("### Closed: ", mux); |           DBG("### Closed: ", mux); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } while (millis() - startMillis < timeout); |     } while (millis() - startMillis < timeout_ms); | ||||||
| finish: | finish: | ||||||
|     if (!index) { |     if (!index) { | ||||||
|       data.trim(); |       data.trim(); | ||||||
| @@ -1029,12 +901,12 @@ finish: | |||||||
|     return index; |     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 r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) |                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||||
|   { |   { | ||||||
|     String data; |     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), |   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 | //#define TINY_GSM_DEBUG Serial | ||||||
|  |  | ||||||
|  | #if !defined(TINY_GSM_RX_BUFFER) | ||||||
|  |   #define TINY_GSM_RX_BUFFER 64 | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #define TINY_GSM_MUX_COUNT 7 | #define TINY_GSM_MUX_COUNT 7 | ||||||
|  |  | ||||||
| #include <TinyGsmCommon.h> | #include <TinyGsmCommon.h> | ||||||
| @@ -37,7 +41,7 @@ enum RegStatus { | |||||||
| }; | }; | ||||||
|  |  | ||||||
|  |  | ||||||
| class TinyGsmUBLOX : public TinyGsmModem | class TinyGsmUBLOX | ||||||
| { | { | ||||||
|  |  | ||||||
| public: | public: | ||||||
| @@ -62,50 +66,30 @@ public: | |||||||
|     sock_connected = false; |     sock_connected = false; | ||||||
|     got_data = false; |     got_data = false; | ||||||
|  |  | ||||||
|     // at->sockets[mux] = this; |     at->sockets[mux] = this; | ||||||
|     //  ^^ TODO: attach the socket here at init?  Or later at connect? |  | ||||||
|     // Currently done inconsistently between modems |  | ||||||
|  |  | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| public: | public: | ||||||
|   virtual int connect(const char *host, uint16_t port) { |   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||||
|     if (sock_connected) { |     stop(); | ||||||
|       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; |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     TINY_GSM_YIELD(); |     TINY_GSM_YIELD(); | ||||||
|     rx.clear(); |     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; |     at->sockets[mux] = this; | ||||||
|     // ^^ TODO: attach the socet after attempting connection or above at init? |  | ||||||
|     // Currently done inconsistently between modems |  | ||||||
|     at->maintain(); |     at->maintain(); | ||||||
|  |  | ||||||
|     return sock_connected; |     return sock_connected; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   virtual int connect(IPAddress ip, uint16_t port) { | TINY_GSM_CLIENT_CONNECT_OVERLOADS() | ||||||
|     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); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual void stop() { |   virtual void stop() { | ||||||
|     TINY_GSM_YIELD(); |     TINY_GSM_YIELD(); | ||||||
| @@ -122,89 +106,15 @@ public: | |||||||
|       at->maintain(); |       at->maintain(); | ||||||
|     } |     } | ||||||
|     at->modemDisconnect(mux); |     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_CLIENT_WRITE() ; | ||||||
|     TINY_GSM_YIELD(); |  | ||||||
|     at->maintain(); |  | ||||||
|     return at->modemSend(buf, size, mux); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual size_t write(uint8_t c) { | TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK() ; | ||||||
|     return write(&c, 1); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual size_t write(const char *str) { | TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK() ; | ||||||
|     if (str == NULL) return 0; |  | ||||||
|     return write((const uint8_t *)str, strlen(str)); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual int available() { | TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() | ||||||
|     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(); } |  | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Extended API |    * Extended API | ||||||
| @@ -233,14 +143,17 @@ public: | |||||||
|   {} |   {} | ||||||
|  |  | ||||||
| public: | public: | ||||||
|   virtual int connect(const char *host, uint16_t port) { |   virtual int connect(const char *host, uint16_t port, int timeout_s) { | ||||||
|     stop(); |     stop(); | ||||||
|     TINY_GSM_YIELD(); |     TINY_GSM_YIELD(); | ||||||
|     rx.clear(); |     rx.clear(); | ||||||
|     // sock_connected = at->modemConnect(host, port, mux, true); |     uint8_t oldMux = mux; | ||||||
|     sock_connected = at->modemConnect(host, port, &mux, true); |     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->sockets[mux] = this; | ||||||
|     // TODO:  When is the socket attached? |  | ||||||
|     at->maintain(); |     at->maintain(); | ||||||
|     return sock_connected; |     return sock_connected; | ||||||
|   } |   } | ||||||
| @@ -250,16 +163,19 @@ public: | |||||||
| public: | public: | ||||||
|  |  | ||||||
|   TinyGsmUBLOX(Stream& stream) |   TinyGsmUBLOX(Stream& stream) | ||||||
|     : TinyGsmModem(stream), stream(stream) |     : stream(stream) | ||||||
|   { |   { | ||||||
|     memset(sockets, 0, sizeof(sockets)); |     memset(sockets, 0, sizeof(sockets)); | ||||||
|     isCatM = false;  // For SARA R4 and N4 series |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Basic functions |    * Basic functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|  |   bool begin(const char* pin = NULL) { | ||||||
|  |     return init(pin); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   bool init(const char* pin = NULL) { |   bool init(const char* pin = NULL) { | ||||||
|     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); |     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); | ||||||
|     if (!testAT()) { |     if (!testAT()) { | ||||||
| @@ -277,14 +193,8 @@ public: | |||||||
| #endif | #endif | ||||||
|     waitResponse(); |     waitResponse(); | ||||||
|  |  | ||||||
|     String name = getModemName(); |     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); |  | ||||||
|     } |  | ||||||
|     int ret = getSimStatus(); |     int ret = getSimStatus(); | ||||||
|     // if the sim isn't ready and a pin has been provided, try to unlock the sim |     // 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) { |     if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) { | ||||||
| @@ -314,59 +224,32 @@ public: | |||||||
|     res2.replace(GSM_NL "OK" GSM_NL, ""); |     res2.replace(GSM_NL "OK" GSM_NL, ""); | ||||||
|     res2.trim(); |     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) { | TINY_GSM_MODEM_SET_BAUD_IPR() | ||||||
|     sendAT(GF("+IPR="), baud); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool testAT(unsigned long timeout = 10000L) { | TINY_GSM_MODEM_TEST_AT() | ||||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { |  | ||||||
|       sendAT(GF("")); |  | ||||||
|       if (waitResponse(200) == 1) return true; |  | ||||||
|       delay(100); |  | ||||||
|     } |  | ||||||
|     return false; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   void maintain() { | TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS() | ||||||
|     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); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool factoryDefault() { |   bool factoryDefault() { | ||||||
|     if (!isCatM) { |     sendAT(GF("+UFACTORY=0,1"));  // No factory restore, erase NVM | ||||||
|       sendAT(GF("+UFACTORY=0,1"));  // No factory restore, erase NVM |     waitResponse(); | ||||||
|       waitResponse(); |     sendAT(GF("+CFUN=16"));   // Reset | ||||||
|       sendAT(GF("+CFUN=16"));   // Reset |     return waitResponse() == 1; | ||||||
|       return waitResponse() == 1; |  | ||||||
|     } |  | ||||||
|     else { |  | ||||||
|       sendAT(GF("&F"));  // Resets the current profile, other NVM not affected |  | ||||||
|       return waitResponse() == 1; |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   String getModemInfo() { | TINY_GSM_MODEM_GET_INFO_ATI() | ||||||
|     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; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool hasSSL() { |   bool hasSSL() { | ||||||
|     return true; |     return true; | ||||||
| @@ -388,14 +271,7 @@ public: | |||||||
|     if (!testAT()) { |     if (!testAT()) { | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
|     if (!isCatM) |     sendAT(GF("+CFUN=16")); | ||||||
|     { |  | ||||||
|       sendAT(GF("+CFUN=16")); |  | ||||||
|     } |  | ||||||
|     else |  | ||||||
|     { |  | ||||||
|       sendAT(GF("+CFUN=15")); |  | ||||||
|     } |  | ||||||
|     if (waitResponse(10000L) != 1) { |     if (waitResponse(10000L) != 1) { | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
| @@ -423,21 +299,9 @@ public: | |||||||
|    * SIM card functions |    * SIM card functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   bool simUnlock(const char *pin) { | TINY_GSM_MODEM_SIM_UNLOCK_CPIN() | ||||||
|     sendAT(GF("+CPIN=\""), pin, GF("\"")); |  | ||||||
|     return waitResponse() == 1; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   String getSimCCID() { | TINY_GSM_MODEM_GET_SIMCCID_CCID() | ||||||
|     sendAT(GF("+CCID")); |  | ||||||
|     if (waitResponse(GF(GSM_NL "+CCID:")) != 1) { |  | ||||||
|       return ""; |  | ||||||
|     } |  | ||||||
|     String res = stream.readStringUntil('\n'); |  | ||||||
|     waitResponse(); |  | ||||||
|     res.trim(); |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   String getIMEI() { |   String getIMEI() { | ||||||
|     sendAT(GF("+CGSN")); |     sendAT(GF("+CGSN")); | ||||||
| @@ -450,8 +314,8 @@ public: | |||||||
|     return res; |     return res; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SimStatus getSimStatus(unsigned long timeout = 10000L) { |   SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { | ||||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { |     for (unsigned long start = millis(); millis() - start < timeout_ms; ) { | ||||||
|       sendAT(GF("+CPIN?")); |       sendAT(GF("+CPIN?")); | ||||||
|       if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { |       if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { | ||||||
|         delay(1000); |         delay(1000); | ||||||
| @@ -469,49 +333,15 @@ public: | |||||||
|     return SIM_ERROR; |     return SIM_ERROR; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   RegStatus getRegistrationStatus() { | TINY_GSM_MODEM_GET_REGISTRATION_XREG(CGREG) | ||||||
|     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; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   String getOperator() { | TINY_GSM_MODEM_GET_OPERATOR_COPS() | ||||||
|     sendAT(GF("+COPS?")); |  | ||||||
|     if (waitResponse(GF(GSM_NL "+COPS:")) != 1) { |  | ||||||
|       return ""; |  | ||||||
|     } |  | ||||||
|     streamSkipUntil('"'); // Skip mode and format |  | ||||||
|     String res = stream.readStringUntil('"'); |  | ||||||
|     waitResponse(); |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * Generic network functions |    * Generic network functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   int16_t getSignalQuality() { | TINY_GSM_MODEM_GET_CSQ() | ||||||
|     sendAT(GF("+CSQ")); |  | ||||||
|     if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) { |  | ||||||
|       return 99; |  | ||||||
|     } |  | ||||||
|     int res = stream.readStringUntil(',').toInt(); |  | ||||||
|     waitResponse(); |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool isNetworkConnected() { |   bool isNetworkConnected() { | ||||||
|     RegStatus s = getRegistrationStatus(); |     RegStatus s = getRegistrationStatus(); | ||||||
| @@ -522,27 +352,12 @@ public: | |||||||
|     else return false; |     else return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool setURAT( uint8_t urat ) { | TINY_GSM_MODEM_WAIT_FOR_NETWORK() | ||||||
|     // 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 |    * GPRS functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { |   bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { | ||||||
|     gprsDisconnect(); |     gprsDisconnect(); | ||||||
|  |  | ||||||
| @@ -551,131 +366,75 @@ public: | |||||||
|       return false; |       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 |     // 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 |     // "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 |     // stack and related AT commands for sockets. | ||||||
|     // all of the other modules. |  | ||||||
|  |  | ||||||
|     if (isCatM) { |     sendAT(GF("+UPSD=0,1,\""), apn, '"');  // Set APN for PSD profile 0 | ||||||
|       if (user && strlen(user) > 0) { |     waitResponse(); | ||||||
|         sendAT(GF("+CGAUTH=1,0,\""), user, GF("\",\""), pwd, '"');  // Set the authentication |  | ||||||
|         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(); |       waitResponse(); | ||||||
|  |  | ||||||
|       sendAT(GF("+CGACT=1,1"));  // activate PDP profile/context 1 |  | ||||||
|       if (waitResponse(150000L) != 1) { |  | ||||||
|         return false; |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       return true; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     else { |     sendAT(GF("+UPSD=0,7,\"0.0.0.0\"")); // Dynamic IP on PSD profile 0 | ||||||
|       sendAT(GF("+UPSD=0,1,\""), apn, '"');  // Set APN for PSD profile 0 |     waitResponse(); | ||||||
|       waitResponse(); |  | ||||||
|  |  | ||||||
|       if (user && strlen(user) > 0) { |     sendAT(GF("+UPSDA=0,3")); // Activate the PDP context associated with profile 0 | ||||||
|         sendAT(GF("+UPSD=0,2,\""), user, '"');  // Set user for PSD profile 0 |     if (waitResponse(360000L) != 1) { | ||||||
|         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) |  | ||||||
|       return false; |       return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     sendAT(GF("+UPSND=0,8")); // Activate PSD profile 0 | ||||||
|  |     if (waitResponse(GF(",8,1")) != 1) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |     waitResponse(); | ||||||
|  |  | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool isGprsConnected() { |   bool gprsDisconnect() { | ||||||
|     sendAT(GF("+CGATT?")); |     sendAT(GF("+UPSDA=0,4"));  // Deactivate the PDP context associated with profile 0 | ||||||
|     if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) { |     if (waitResponse(360000L) != 1) { | ||||||
|       return false; |       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 |    * IP Address functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   String getLocalIP() { |   String getLocalIP() { | ||||||
|     // LTE-M and NB-IoT modules do not support UPSx commands |     sendAT(GF("+UPSND=0,0")); | ||||||
|     if (isCatM) { |     if (waitResponse(GF(GSM_NL "+UPSND:")) != 1) { | ||||||
|       sendAT(GF("+CGPADDR")); |       return ""; | ||||||
|       if (waitResponse(GF(GSM_NL "+CGPADDR:")) != 1) { |  | ||||||
|         return ""; |  | ||||||
|       } |  | ||||||
|       streamSkipUntil(',');  // Skip context id |  | ||||||
|       String res = stream.readStringUntil('\r'); |  | ||||||
|       if (waitResponse() != 1) { |  | ||||||
|         return ""; |  | ||||||
|       } |  | ||||||
|       return res; |  | ||||||
|     } |     } | ||||||
|     else { |     streamSkipUntil(',');  // Skip PSD profile | ||||||
|       sendAT(GF("+UPSND=0,0")); |     streamSkipUntil('\"'); // Skip request type | ||||||
|       if (waitResponse(GF(GSM_NL "+UPSND:")) != 1) { |     String res = stream.readStringUntil('\"'); | ||||||
|         return ""; |     if (waitResponse() != 1) { | ||||||
|       } |       return ""; | ||||||
|       streamSkipUntil(',');  // Skip PSD profile |  | ||||||
|       streamSkipUntil('\"'); // Skip request type |  | ||||||
|       String res = stream.readStringUntil('\"'); |  | ||||||
|       if (waitResponse() != 1) { |  | ||||||
|         return ""; |  | ||||||
|       } |  | ||||||
|       return res; |  | ||||||
|     } |     } | ||||||
|  |     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; |   uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||||
|  |  | ||||||
|   int8_t getBattPercent() { |   int8_t getBattPercent() { | ||||||
| @@ -745,13 +505,26 @@ public: | |||||||
|     return res; |     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 |    * Client related functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
| protected: | 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 |     sendAT(GF("+USOCR=6"));  // create a socket | ||||||
|     if (waitResponse(GF(GSM_NL "+USOCR:")) != 1) {  // reply is +USOCR: ## of socket created |     if (waitResponse(GF(GSM_NL "+USOCR:")) != 1) {  // reply is +USOCR: ## of socket created | ||||||
|       return false; |       return false; | ||||||
| @@ -776,22 +549,23 @@ protected: | |||||||
|     // TODO:  Use faster "asynchronous" connection? |     // TODO:  Use faster "asynchronous" connection? | ||||||
|     // We would have to wait for the +UUSOCO URC to verify connection |     // We would have to wait for the +UUSOCO URC to verify connection | ||||||
|     sendAT(GF("+USOCO="), *mux, ",\"", host, "\",", port); |     sendAT(GF("+USOCO="), *mux, ",\"", host, "\",", port); | ||||||
|     int rsp = waitResponse(120000L); |     int rsp = waitResponse(timeout_ms); | ||||||
|     return (1 == rsp); |     return (1 == rsp); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool modemDisconnect(uint8_t mux) { |   bool modemDisconnect(uint8_t mux) { | ||||||
|     TINY_GSM_YIELD(); |     TINY_GSM_YIELD(); | ||||||
|     if (!modemGetConnected(mux)) return true; |     if (!modemGetConnected(mux)) { | ||||||
|     if (isCatM) {  //  These modems allow a faster "asynchronous" close |       sockets[mux]->sock_connected = false; | ||||||
|       sendAT(GF("+USOCL="), mux, GF(",1")); |       return true; | ||||||
|       int rsp = waitResponse(120000L); |  | ||||||
|       return (1 == rsp);  // but it still can take up to 120s to get a response |  | ||||||
|     } |     } | ||||||
|     else {  // no async close |     bool success; | ||||||
|       sendAT(GF("+USOCL="), mux); |     sendAT(GF("+USOCL="), mux); | ||||||
|       return (1 == waitResponse()); |     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) { |   int16_t modemSend(const void* buff, size_t len, uint8_t mux) { | ||||||
| @@ -808,8 +582,7 @@ protected: | |||||||
|     } |     } | ||||||
|     streamSkipUntil(','); // Skip mux |     streamSkipUntil(','); // Skip mux | ||||||
|     int sent = stream.readStringUntil('\n').toInt(); |     int sent = stream.readStringUntil('\n').toInt(); | ||||||
|     waitResponse(); |     waitResponse();  // sends back OK after the confirmation of number sent | ||||||
|     maintain();  // look for a very quick response from the remote |  | ||||||
|     return sent; |     return sent; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -824,18 +597,16 @@ protected: | |||||||
|     streamSkipUntil('\"'); |     streamSkipUntil('\"'); | ||||||
|  |  | ||||||
|     for (size_t i=0; i<len; i++) { |     for (size_t i=0; i<len; i++) { | ||||||
|       while (!stream.available()) { TINY_GSM_YIELD(); } |       TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT | ||||||
|       char c = stream.read(); |  | ||||||
|       sockets[mux]->rx.put(c); |  | ||||||
|     } |     } | ||||||
|     streamSkipUntil('\"'); |     streamSkipUntil('\"'); | ||||||
|     waitResponse(); |     waitResponse(); | ||||||
|     DBG("### READ:", len, "from", mux); |     DBG("### READ:", len, "from", mux); | ||||||
|     maintain();  // Listen for a close or other URC |  | ||||||
|     return len; |     return len; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   size_t modemGetAvailable(uint8_t mux) { |   size_t modemGetAvailable(uint8_t mux) { | ||||||
|  |     // NOTE:  Querying a closed socket gives an error "operation not allowed" | ||||||
|     sendAT(GF("+USORD="), mux, ",0"); |     sendAT(GF("+USORD="), mux, ",0"); | ||||||
|     size_t result = 0; |     size_t result = 0; | ||||||
|     uint8_t res = waitResponse(GF(GSM_NL "+USORD:")); |     uint8_t res = waitResponse(GF(GSM_NL "+USORD:")); | ||||||
| @@ -844,17 +615,17 @@ protected: | |||||||
|     if (res == 1) { |     if (res == 1) { | ||||||
|       streamSkipUntil(','); // Skip mux |       streamSkipUntil(','); // Skip mux | ||||||
|       result = stream.readStringUntil('\n').toInt(); |       result = stream.readStringUntil('\n').toInt(); | ||||||
|       DBG("### DATA AVAILABLE:", result, "on", mux); |       // if (result) DBG("### DATA AVAILABLE:", result, "on", mux); | ||||||
|       waitResponse(); |       waitResponse(); | ||||||
|     } |     } | ||||||
|     if (!result && res != 2 && res != 3) {  // Don't check modemGetConnected after an error |     if (!result && res != 2 && res != 3) {  // Don't check modemGetConnected after an error | ||||||
|       sockets[mux]->sock_connected = modemGetConnected(mux); |       sockets[mux]->sock_connected = modemGetConnected(mux); | ||||||
|     } |     } | ||||||
|     maintain();  // Listen for a close or other URC |  | ||||||
|     return result; |     return result; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool modemGetConnected(uint8_t mux) { |   bool modemGetConnected(uint8_t mux) { | ||||||
|  |     // NOTE:  Querying a closed socket gives an error "operation not allowed" | ||||||
|     sendAT(GF("+USOCTL="), mux, ",10"); |     sendAT(GF("+USOCTL="), mux, ",10"); | ||||||
|     uint8_t res = waitResponse(GF(GSM_NL "+USOCTL:")); |     uint8_t res = waitResponse(GF(GSM_NL "+USOCTL:")); | ||||||
|     if (res != 1) |     if (res != 1) | ||||||
| @@ -876,7 +647,6 @@ protected: | |||||||
|     // 9: the socket is in LAST_ACK status |     // 9: the socket is in LAST_ACK status | ||||||
|     // 10: the socket is in TIME_WAIT status |     // 10: the socket is in TIME_WAIT status | ||||||
|     waitResponse(); |     waitResponse(); | ||||||
|     maintain();  // Listen for a close or other URC |  | ||||||
|     return (result != 0); |     return (result != 0); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -886,16 +656,10 @@ public: | |||||||
|    Utilities |    Utilities | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   template<typename... Args> | TINY_GSM_MODEM_STREAM_UTILITIES() | ||||||
|   void sendAT(Args... cmd) { |  | ||||||
|     streamWrite("AT", cmd..., GSM_NL); |  | ||||||
|     stream.flush(); |  | ||||||
|     TINY_GSM_YIELD(); |  | ||||||
|     //DBG("### AT:", cmd...); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // TODO: Optimize this! |   // 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 r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||||
|                        GsmConstStr r3=GFP(GSM_CME_ERROR), GsmConstStr r4=NULL, GsmConstStr r5=NULL) |                        GsmConstStr r3=GFP(GSM_CME_ERROR), GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||||
|   { |   { | ||||||
| @@ -937,17 +701,17 @@ public: | |||||||
|             sockets[mux]->sock_available = len; |             sockets[mux]->sock_available = len; | ||||||
|           } |           } | ||||||
|           data = ""; |           data = ""; | ||||||
|           DBG("### Got Data:", len, "on", mux); |           DBG("### URC Data Received:", len, "on", mux); | ||||||
|         } else if (data.endsWith(GF(GSM_NL "+UUSOCL:"))) { |         } else if (data.endsWith(GF(GSM_NL "+UUSOCL:"))) { | ||||||
|           int mux = stream.readStringUntil('\n').toInt(); |           int mux = stream.readStringUntil('\n').toInt(); | ||||||
|           if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { |           if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { | ||||||
|             sockets[mux]->sock_connected = false; |             sockets[mux]->sock_connected = false; | ||||||
|           } |           } | ||||||
|           data = ""; |           data = ""; | ||||||
|           DBG("### Closed: ", mux); |           DBG("### URC Sock Closed: ", mux); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } while (millis() - startMillis < timeout); |     } while (millis() - startMillis < timeout_ms); | ||||||
| finish: | finish: | ||||||
|     if (!index) { |     if (!index) { | ||||||
|       data.trim(); |       data.trim(); | ||||||
| @@ -960,12 +724,12 @@ finish: | |||||||
|     return index; |     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 r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||||
|                        GsmConstStr r3=GFP(GSM_CME_ERROR), GsmConstStr r4=NULL, GsmConstStr r5=NULL) |                        GsmConstStr r3=GFP(GSM_CME_ERROR), GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||||
|   { |   { | ||||||
|     String data; |     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), |   uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||||
| @@ -979,7 +743,6 @@ public: | |||||||
|  |  | ||||||
| protected: | protected: | ||||||
|   GsmClient*    sockets[TINY_GSM_MUX_COUNT]; |   GsmClient*    sockets[TINY_GSM_MUX_COUNT]; | ||||||
|   bool          isCatM; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -24,6 +24,20 @@ | |||||||
| static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; | 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_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 { | enum SimStatus { | ||||||
|   SIM_ERROR = 0, |   SIM_ERROR = 0, | ||||||
|   SIM_READY = 1, |   SIM_READY = 1, | ||||||
| @@ -49,7 +63,7 @@ enum XBeeType { | |||||||
| }; | }; | ||||||
|  |  | ||||||
|  |  | ||||||
| class TinyGsmXBee : public TinyGsmModem | class TinyGsmXBee | ||||||
| { | { | ||||||
|  |  | ||||||
| public: | public: | ||||||
| @@ -77,31 +91,30 @@ public: | |||||||
|   } |   } | ||||||
|  |  | ||||||
| public: | public: | ||||||
|   // NOTE:  The XBee saves all paramter information in flash.  When you turn it |   // NOTE:  The XBee saves all connection information (ssid/pwd or apn AND last used IP address) | ||||||
|   // on it immediately prepares to re-connect to whatever was last connected to. |   // in flash (NVM).  When you turn it on it immediately prepares to re-connect to whatever was | ||||||
|   // All the modemConnect() function does is tell it the paramters to put into |   // last set.  The TCP connection itself is not opened until you attempt to send data. | ||||||
|   // flash.  The connection itself is not opened until you attempt to send data. |  | ||||||
|   // Because all settings are saved to flash, it is possible (or likely) that |   // 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. |   // you could send data even if you haven't "made" any connection. | ||||||
|   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 |     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, timeout_s); | ||||||
|       sock_connected = at->modemConnect(host, port, mux, false); |  | ||||||
|       at->writeChanges(); |  | ||||||
|       at->exitCommand(); |  | ||||||
|     } |  | ||||||
|     return sock_connected; |     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 |     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, timeout_s); | ||||||
|       sock_connected = at->modemConnect(ip, port, mux, false); |  | ||||||
|       at->writeChanges(); |  | ||||||
|       at->exitCommand(); |  | ||||||
|     } |  | ||||||
|     return sock_connected; |     return sock_connected; | ||||||
|   } |   } | ||||||
|  |   virtual int connect(IPAddress ip, uint16_t port) { | ||||||
|  |     return connect(ip, port, 75); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   virtual void stop() { |   virtual void stop() { | ||||||
|     at->streamClear();  // Empty anything in the buffer |     at->streamClear();  // Empty anything in the buffer | ||||||
| @@ -196,12 +209,6 @@ public: | |||||||
|     if (available()) { |     if (available()) { | ||||||
|       return true; |       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; |     return sock_connected; | ||||||
|   } |   } | ||||||
|   virtual operator bool() { return connected(); } |   virtual operator bool() { return connected(); } | ||||||
| @@ -230,23 +237,17 @@ public: | |||||||
|   {} |   {} | ||||||
|  |  | ||||||
| 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 |     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, timeout_s); | ||||||
|       sock_connected = at->modemConnect(host, port, mux, true); |  | ||||||
|       at->writeChanges(); |  | ||||||
|       at->exitCommand(); |  | ||||||
|     } |  | ||||||
|     return sock_connected; |     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 |     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, timeout_s); | ||||||
|       sock_connected = at->modemConnect(ip, port, mux, false); |  | ||||||
|       at->writeChanges(); |  | ||||||
|       at->exitCommand(); |  | ||||||
|     } |  | ||||||
|     return sock_connected; |     return sock_connected; | ||||||
|   } |   } | ||||||
| }; | }; | ||||||
| @@ -255,24 +256,26 @@ public: | |||||||
| public: | public: | ||||||
|  |  | ||||||
|   TinyGsmXBee(Stream& stream) |   TinyGsmXBee(Stream& stream) | ||||||
|     : TinyGsmModem(stream), stream(stream) |     : stream(stream) | ||||||
|   { |   { | ||||||
|       beeType = XBEE_UNKNOWN;  // Start not knowing what kind of bee it is |       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 |       guardTime = TINY_GSM_XBEE_GUARD_TIME;  // Start with the default guard time of 1 second | ||||||
|       resetPin = -1; |       resetPin = -1; | ||||||
|       savedIP = IPAddress(0,0,0,0); |       savedIP = IPAddress(0,0,0,0); | ||||||
|       savedHost = ""; |       savedHost = ""; | ||||||
|  |       inCommandMode = false; | ||||||
|       memset(sockets, 0, sizeof(sockets)); |       memset(sockets, 0, sizeof(sockets)); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   TinyGsmXBee(Stream& stream, int8_t resetPin) |   TinyGsmXBee(Stream& stream, int8_t resetPin) | ||||||
|     : TinyGsmModem(stream), stream(stream) |     : stream(stream) | ||||||
|   { |   { | ||||||
|       beeType = XBEE_UNKNOWN;  // Start not knowing what kind of bee it is |       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 |       guardTime = TINY_GSM_XBEE_GUARD_TIME;  // Start with the default guard time of 1 second | ||||||
|       this->resetPin = resetPin; |       this->resetPin = resetPin; | ||||||
|       savedIP = IPAddress(0,0,0,0); |       savedIP = IPAddress(0,0,0,0); | ||||||
|       savedHost = ""; |       savedHost = ""; | ||||||
|  |       inCommandMode = false; | ||||||
|       memset(sockets, 0, sizeof(sockets)); |       memset(sockets, 0, sizeof(sockets)); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -280,6 +283,10 @@ public: | |||||||
|    * Basic functions |    * Basic functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|  |   bool begin(const char* pin = NULL) { | ||||||
|  |     return init(pin); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   bool init(const char* pin = NULL) { |   bool init(const char* pin = NULL) { | ||||||
|     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); |     DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); | ||||||
|  |  | ||||||
| @@ -288,20 +295,26 @@ public: | |||||||
|       digitalWrite(resetPin, HIGH); |       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 |     sendAT(GF("AP0"));  // Put in transparent mode | ||||||
|     bool ret_val = waitResponse() == 1; |     bool ret_val = waitResponse() == 1; | ||||||
|     ret_val &= writeChanges(); |  | ||||||
|  |  | ||||||
|     sendAT(GF("GT64")); // shorten the guard time to 100ms |     sendAT(GF("GT64")); // shorten the guard time to 100ms | ||||||
|     ret_val &= waitResponse(); |     ret_val &= waitResponse() == 1; | ||||||
|     ret_val &= writeChanges(); |  | ||||||
|     if (ret_val) guardTime = 110; |     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"; |     getSeries();  // Get the "Hardware Series"; | ||||||
|  |  | ||||||
|     exitCommand(); |     XBEE_COMMAND_END_DECORATOR | ||||||
|  |  | ||||||
|     return ret_val; |     return ret_val; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -310,7 +323,7 @@ public: | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   void setBaud(unsigned long baud) { |   void setBaud(unsigned long baud) { | ||||||
|     if (!commandMode()) return; |     XBEE_COMMAND_START_DECORATOR(5, ) | ||||||
|     switch(baud) |     switch(baud) | ||||||
|     { |     { | ||||||
|       case 2400: sendAT(GF("BD1")); break; |       case 2400: sendAT(GF("BD1")); break; | ||||||
| @@ -331,40 +344,48 @@ public: | |||||||
|     } |     } | ||||||
|     waitResponse(); |     waitResponse(); | ||||||
|     writeChanges(); |     writeChanges(); | ||||||
|     exitCommand(); |     XBEE_COMMAND_END_DECORATOR | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool testAT(unsigned long timeout = 10000L) { |   bool testAT(unsigned long timeout_ms = 10000L) { | ||||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { |     unsigned long start = millis(); | ||||||
|       if (commandMode()) |     bool success = false; | ||||||
|       { |     while (!success && millis() - start < timeout_ms) { | ||||||
|  |       if (!inCommandMode) { | ||||||
|  |         success = commandMode(); | ||||||
|  |         if (success) exitCommand(); | ||||||
|  |       } | ||||||
|  |       else { | ||||||
|           sendAT(); |           sendAT(); | ||||||
|           if (waitResponse(200) == 1) { |           if (waitResponse(200) == 1) { | ||||||
|               exitCommand(); |               success =  true; | ||||||
|               return 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() { |   void maintain() { | ||||||
|     // this only happens OUTSIDE command mode, so if we're getting characters |     // this only happens OUTSIDE command mode, so if we're getting characters | ||||||
|     // they should be data received from the TCP connection |     // they should be data received from the TCP connection | ||||||
|     // TINY_GSM_YIELD(); |     // TINY_GSM_YIELD(); | ||||||
|     // while (stream.available()) { |     // if (!inCommandMode) { | ||||||
|     //   char c = stream.read(); |     //   while (stream.available()) { | ||||||
|     //   if (c > 0) sockets[0]->rx.put(c); |     //     char c = stream.read(); | ||||||
|  |     //     if (c > 0) sockets[0]->rx.put(c); | ||||||
|  |     //   } | ||||||
|     // } |     // } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool factoryDefault() { |   bool factoryDefault() { | ||||||
|     if (!commandMode()) return false;  // Return immediately |     XBEE_COMMAND_START_DECORATOR(5, false) | ||||||
|     sendAT(GF("RE")); |     sendAT(GF("RE")); | ||||||
|     bool ret_val = waitResponse() == 1; |     bool ret_val = waitResponse() == 1; | ||||||
|     ret_val &= writeChanges(); |     ret_val &= writeChanges(); | ||||||
|     exitCommand(); |     XBEE_COMMAND_END_DECORATOR | ||||||
|     // Make sure the guard time for the modem object is set back to default |     // Make sure the guard time for the modem object is set back to default | ||||||
|     // otherwise communication would fail after the reset |     // otherwise communication would fail after the reset | ||||||
|     guardTime = 1010; |     guardTime = 1010; | ||||||
| @@ -372,14 +393,7 @@ public: | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   String getModemInfo() { |   String getModemInfo() { | ||||||
|     String modemInf = ""; |     return sendATGetString(GF("HS")); | ||||||
|     if (!commandMode()) return modemInf;  // Try up to 10 times for the init |  | ||||||
|  |  | ||||||
|     sendAT(GF("HS"));  // Get the "Hardware Series" |  | ||||||
|     modemInf += readResponseString(); |  | ||||||
|  |  | ||||||
|     exitCommand(); |  | ||||||
|     return modemInf; |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool hasSSL() { |   bool hasSSL() { | ||||||
| @@ -428,7 +442,9 @@ public: | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool restart() { |   bool restart() { | ||||||
|  |  | ||||||
|     if (!commandMode()) return false;  // Return immediately |     if (!commandMode()) return false;  // Return immediately | ||||||
|  |  | ||||||
|     if (beeType == XBEE_UNKNOWN) getSeries();  // how we restart depends on this |     if (beeType == XBEE_UNKNOWN) getSeries();  // how we restart depends on this | ||||||
|  |  | ||||||
|     if (beeType != XBEE_S6B_WIFI) { |     if (beeType != XBEE_S6B_WIFI) { | ||||||
| @@ -440,9 +456,10 @@ public: | |||||||
|  |  | ||||||
|     sendAT(GF("FR")); |     sendAT(GF("FR")); | ||||||
|     if (waitResponse() != 1) return exitAndFail(); |     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 |     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 |     // Wait until reboot complete and responds to command mode call again | ||||||
|     for (unsigned long start = millis(); millis() - start < 60000L; ) { |     for (unsigned long start = millis(); millis() - start < 60000L; ) { | ||||||
| @@ -462,7 +479,7 @@ public: | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   void setupPinSleep(bool maintainAssociation = false) { |   void setupPinSleep(bool maintainAssociation = false) { | ||||||
|     if (!commandMode()) return;  // Return immediately |     XBEE_COMMAND_START_DECORATOR(5, ) | ||||||
|  |  | ||||||
|     if (beeType == XBEE_UNKNOWN) getSeries();  // Command depends on series |     if (beeType == XBEE_UNKNOWN) getSeries();  // Command depends on series | ||||||
|  |  | ||||||
| @@ -481,7 +498,7 @@ public: | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     writeChanges(); |     writeChanges(); | ||||||
|     exitCommand(); |     XBEE_COMMAND_END_DECORATOR | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool poweroff() {  // Not supported |   bool poweroff() {  // Not supported | ||||||
| @@ -501,32 +518,27 @@ public: | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   String getSimCCID() { |   String getSimCCID() { | ||||||
|     if (!commandMode()) return "";  // Return immediately |     return sendATGetString(GF("S#")); | ||||||
|     sendAT(GF("S#")); |  | ||||||
|     String res = readResponseString(); |  | ||||||
|     exitCommand(); |  | ||||||
|     return res; |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   String getIMEI() { |   String getIMEI() { | ||||||
|     if (!commandMode()) return "";  // Return immediately |     return sendATGetString(GF("IM")); | ||||||
|     sendAT(GF("IM")); |  | ||||||
|     String res = readResponseString(); |  | ||||||
|     exitCommand(); |  | ||||||
|     return res; |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SimStatus getSimStatus(unsigned long timeout = 10000L) { |   SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { | ||||||
|     return SIM_READY;  // unsupported |     return SIM_READY;  // unsupported | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   RegStatus getRegistrationStatus() { |   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 |     if (beeType == XBEE_UNKNOWN) getSeries();  // Need to know the bee type to interpret response | ||||||
|  |  | ||||||
|     sendAT(GF("AI")); |     sendAT(GF("AI")); | ||||||
|     int16_t intRes = readResponseInt(); |     int16_t intRes = readResponseInt(10000L); | ||||||
|     RegStatus stat = REG_UNKNOWN; |     RegStatus stat = REG_UNKNOWN; | ||||||
|  |  | ||||||
|     switch (beeType){ |     switch (beeType){ | ||||||
| @@ -598,16 +610,12 @@ public: | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     exitCommand(); |     XBEE_COMMAND_END_DECORATOR | ||||||
|     return stat; |     return stat; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   String getOperator() { |   String getOperator() { | ||||||
|     if (!commandMode()) return "";  // Return immediately |     return sendATGetString(GF("MN")); | ||||||
|     sendAT(GF("MN")); |  | ||||||
|     String res = readResponseString(); |  | ||||||
|     exitCommand(); |  | ||||||
|     return res; |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  /* |  /* | ||||||
| @@ -615,12 +623,17 @@ public: | |||||||
|   */ |   */ | ||||||
|  |  | ||||||
|   int16_t getSignalQuality() { |   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_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 |     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 |     else sendAT(GF("DB"));  // ask for the cell strength in dBm | ||||||
|     int16_t intRes = readResponseInt(); |     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 == 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 |     if (beeType == XBEE_S6B_WIFI) return -93 + intRes;  // the maximum sensitivity is -93dBm | ||||||
|     else return -1*intRes; // need to convert to negative number |     else return -1*intRes; // need to convert to negative number | ||||||
| @@ -631,52 +644,60 @@ public: | |||||||
|     return (s == REG_OK); |     return (s == REG_OK); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool waitForNetwork(unsigned long timeout = 60000L) { |   bool waitForNetwork(unsigned long timeout_ms = 60000L) { | ||||||
|     for (unsigned long start = millis(); millis() - start < timeout; ) { |     bool retVal = false; | ||||||
|  |     XBEE_COMMAND_START_DECORATOR(5, false) | ||||||
|  |     for (unsigned long start = millis(); millis() - start < timeout_ms; ) { | ||||||
|       if (isNetworkConnected()) { |       if (isNetworkConnected()) { | ||||||
|         return true; |         retVal = true; | ||||||
|  |         break; | ||||||
|       } |       } | ||||||
|       delay(250);  // per Neil H. - more stable with delay |       delay(250);  // per Neil H. - more stable with delay | ||||||
|     } |     } | ||||||
|     return false; |     XBEE_COMMAND_END_DECORATOR | ||||||
|  |     return retVal; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * WiFi functions |    * WiFi functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   bool networkConnect(const char* ssid, const char* pwd) { |   bool networkConnect(const char* ssid, const char* pwd) { | ||||||
|  |  | ||||||
|     if (!commandMode()) return false;  // return immediately |     bool retVal = true; | ||||||
|     //nh For no pwd don't set setscurity or pwd |     XBEE_COMMAND_START_DECORATOR(5, false) | ||||||
|     if (NULL == ssid ) return exitAndFail(); |  | ||||||
|  |  | ||||||
|     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 |       sendAT(GF("EE"), 2);  // Set security to WPA2 | ||||||
|       if (waitResponse() != 1) return exitAndFail(); |       if (waitResponse() != 1) retVal = false; | ||||||
|       sendAT(GF("PK"), pwd); |       sendAT(GF("PK"), pwd); | ||||||
|     } else { |     } else { | ||||||
|       sendAT(GF("EE"), 0);  // Set No security |       sendAT(GF("EE"), 0);  // Set No security | ||||||
|     } |     } | ||||||
|     if (waitResponse() != 1) return exitAndFail(); |     if (waitResponse() != 1) retVal = false; | ||||||
|  |  | ||||||
|     sendAT(GF("ID"), ssid); |     sendAT(GF("ID"), ssid); | ||||||
|     if (waitResponse() != 1) return exitAndFail(); |     if (waitResponse() != 1) retVal = false; | ||||||
|  |  | ||||||
|     if (!writeChanges()) return exitAndFail(); |     if (!writeChanges()) retVal = false; | ||||||
|     exitCommand(); |  | ||||||
|  |  | ||||||
|     return true; |     XBEE_COMMAND_END_DECORATOR | ||||||
|  |  | ||||||
|  |     return retVal; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool networkDisconnect() { |   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 |     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. |     // allow the same ssid to re-join without rebooting the module. | ||||||
|     int8_t res = (1 == waitResponse(5000)); |     int8_t res = (1 == waitResponse(5000)); | ||||||
|     writeChanges(); |     writeChanges(); | ||||||
|     exitCommand(); |     XBEE_COMMAND_END_DECORATOR | ||||||
|     return res; |     return res; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -685,37 +706,42 @@ public: | |||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   String getLocalIP() { |   String getLocalIP() { | ||||||
|     if (!commandMode()) return "";  // Return immediately |     XBEE_COMMAND_START_DECORATOR(5, "") | ||||||
|     sendAT(GF("MY")); |     sendAT(GF("MY")); | ||||||
|     String IPaddr; IPaddr.reserve(16); |     String IPaddr; IPaddr.reserve(16); | ||||||
|     // wait for the response - this response can be very slow |     // wait for the response - this response can be very slow | ||||||
|     IPaddr = readResponseString(30000); |     IPaddr = readResponseString(30000); | ||||||
|     exitCommand(); |     XBEE_COMMAND_END_DECORATOR | ||||||
|     IPaddr.trim(); |     IPaddr.trim(); | ||||||
|     return IPaddr; |     return IPaddr; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   IPAddress localIP() { | ||||||
|  |     return TinyGsmIpFromString(getLocalIP()); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|    * GPRS functions |    * GPRS functions | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|   bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { |   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 |     sendAT(GF("AN"), apn);  // Set the APN | ||||||
|     waitResponse(); |     bool success = waitResponse() == 1; | ||||||
|     writeChanges(); |     writeChanges(); | ||||||
|     exitCommand(); |     XBEE_COMMAND_END_DECORATOR | ||||||
|     return true; |     return success; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool gprsDisconnect() { |   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 |     sendAT(GF("AM1"));  // Cheating and disconnecting by turning on airplane mode | ||||||
|     int8_t res = (1 == waitResponse(5000)); |     int8_t res = (1 == waitResponse(5000)); | ||||||
|     writeChanges(); |     writeChanges(); | ||||||
|     sendAT(GF("AM0"));  // Airplane mode off |     sendAT(GF("AM0"));  // Airplane mode off | ||||||
|     waitResponse(5000); |     waitResponse(5000); | ||||||
|     writeChanges(); |     writeChanges(); | ||||||
|     exitCommand(); |     XBEE_COMMAND_END_DECORATOR | ||||||
|     return res; |     return res; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -738,11 +764,14 @@ public: | |||||||
|     if (waitResponse() !=1) return exitAndFail(); |     if (waitResponse() !=1) return exitAndFail(); | ||||||
|     sendAT(GF("TDD"));  // Set the text delimiter to the standard 0x0D (carriage return) |     sendAT(GF("TDD"));  // Set the text delimiter to the standard 0x0D (carriage return) | ||||||
|     if (waitResponse() !=1) return exitAndFail(); |     if (waitResponse() !=1) return exitAndFail(); | ||||||
|     if (!writeChanges()) return exitAndFail(); |  | ||||||
|  |  | ||||||
|  |     if (!writeChanges()) return exitAndFail(); | ||||||
|  |     // Get out of command mode to actually send the text | ||||||
|     exitCommand(); |     exitCommand(); | ||||||
|  |  | ||||||
|     streamWrite(text); |     streamWrite(text); | ||||||
|     stream.write((char)0x0D);  // close off with the carriage return |     stream.write((char)0x0D);  // close off with the carriage return | ||||||
|  |  | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -753,12 +782,25 @@ public: | |||||||
|   String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE; |   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; |   uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE; | ||||||
|  |  | ||||||
|   int8_t getBattPercent() 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 |    * Client related functions | ||||||
| @@ -766,51 +808,66 @@ public: | |||||||
|  |  | ||||||
| protected: | protected: | ||||||
|  |  | ||||||
|   IPAddress getHostIP(const char* host) { |   IPAddress getHostIP(const char* host, int timeout_s = 45) { | ||||||
|     String strIP; strIP.reserve(16); |     String strIP; strIP.reserve(16); | ||||||
|     unsigned long startMillis = millis(); |     unsigned long startMillis = millis(); | ||||||
|  |     uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; | ||||||
|     bool gotIP = false; |     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 |     // 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 |     // 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); |       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 = stream.readStringUntil('\r');  // read result | ||||||
|       strIP.trim(); |       strIP.trim(); | ||||||
|       if (!strIP.endsWith(GF("ERROR"))) { |       if (strIP != "" && strIP != GF("ERROR")) { | ||||||
|         gotIP = true; |         gotIP = true; | ||||||
|         break; |         break; | ||||||
|       } |       } | ||||||
|       delay(2500);  // wait a bit before trying again |       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); |       return TinyGsmIpFromString(strIP); | ||||||
|     } |     } | ||||||
|     else return IPAddress(0,0,0,0); |     else return IPAddress(0,0,0,0); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool modemConnect(const char* host, uint16_t port, uint8_t mux = 0, bool ssl = false) { |   bool modemConnect(const char* host, uint16_t port, uint8_t mux = 0, | ||||||
|     // If requested host is the same as the previous one and we already |                     bool ssl = false, int timeout_s = 75) | ||||||
|     // have a valid IP address, we don't have to do anything. |   { | ||||||
|     if (this->savedHost == String(host) && savedIP != IPAddress(0,0,0,0)) { |     unsigned long startMillis = millis(); | ||||||
|       return true; |     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 |     // If it's a new host or we dont' have a good IP, we need to do a DNS | ||||||
|     this->savedHost = String(host); |     // search for the IP to connect to | ||||||
|     savedIP = getHostIP(host);  // This will return 0.0.0.0 if lookup fails |     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 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 |     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 |     savedIP = ip;  // Set the newly requested IP address | ||||||
|     bool success = true; |     bool success = true; | ||||||
|  |     uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; | ||||||
|  |     XBEE_COMMAND_START_DECORATOR(5, false) | ||||||
|     String host; host.reserve(16); |     String host; host.reserve(16); | ||||||
|     host += ip[0]; |     host += ip[0]; | ||||||
|     host += "."; |     host += "."; | ||||||
| @@ -819,6 +876,7 @@ protected: | |||||||
|     host += ip[2]; |     host += ip[2]; | ||||||
|     host += "."; |     host += "."; | ||||||
|     host += ip[3]; |     host += ip[3]; | ||||||
|  |  | ||||||
|     if (ssl) { |     if (ssl) { | ||||||
|       sendAT(GF("IP"), 4);  // Put in SSL over TCP communication mode |       sendAT(GF("IP"), 4);  // Put in SSL over TCP communication mode | ||||||
|       success &= (1 == waitResponse()); |       success &= (1 == waitResponse()); | ||||||
| @@ -826,10 +884,21 @@ protected: | |||||||
|       sendAT(GF("IP"), 1);  // Put in TCP mode |       sendAT(GF("IP"), 1);  // Put in TCP mode | ||||||
|       success &= (1 == waitResponse()); |       success &= (1 == waitResponse()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     sendAT(GF("DL"), host);  // Set the "Destination Address Low" |     sendAT(GF("DL"), host);  // Set the "Destination Address Low" | ||||||
|     success &= (1 == waitResponse()); |     success &= (1 == waitResponse()); | ||||||
|     sendAT(GF("DE"), String(port, HEX));  // Set the destination port |     sendAT(GF("DE"), String(port, HEX));  // Set the destination port | ||||||
|     success &= (1 == waitResponse()); |     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; |     return success; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -844,36 +913,37 @@ protected: | |||||||
|   // really be open, but no data has yet been sent.  We return this unknown value |   // 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. |   // as true so there's a possibility it's wrong. | ||||||
|   bool modemGetConnected() { |   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 the IP address is 0, it's not valid so we can't be connected | ||||||
|     if (savedIP == IPAddress(0,0,0,0)) return false; |     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 |     // Verify that we're connected to the *right* IP address | ||||||
|     // We might be connected - but to the wrong thing |     // We might be connected - but to the wrong thing | ||||||
|     // NOTE:  In transparent mode, there is only one connection possible - no multiplex |     // NOTE:  In transparent mode, there is only one connection possible - no multiplex | ||||||
|     String strIP; strIP.reserve(16); |     // String strIP; strIP.reserve(16); | ||||||
|     sendAT(GF("DL")); |     // sendAT(GF("DL")); | ||||||
|     strIP = stream.readStringUntil('\r');  // read result |     // strIP = stream.readStringUntil('\r');  // read result | ||||||
|     if (TinyGsmIpFromString(strIP) != savedIP) return exitAndFail(); |     // if (TinyGsmIpFromString(strIP) != savedIP) return exitAndFail(); | ||||||
|  |  | ||||||
|     if (beeType == XBEE_UNKNOWN) getSeries();  // Need to know the bee type to interpret response |     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 |     switch (beeType){  // The wifi be can only say if it's connected to the netowrk | ||||||
|       case XBEE_S6B_WIFI: { |       case XBEE_S6B_WIFI: { | ||||||
|         RegStatus s = getRegistrationStatus(); |         RegStatus s = getRegistrationStatus(); | ||||||
|  |         XBEE_COMMAND_END_DECORATOR | ||||||
|         if (s != REG_OK) { |         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 |         return (s == REG_OK);  // if it's connected, we hope the sockets are too | ||||||
|       } |       } | ||||||
|       default: {  // Cellular XBee's |       default: {  // Cellular XBee's | ||||||
|         sendAT(GF("CI")); |         sendAT(GF("CI")); | ||||||
|         int16_t intRes = readResponseInt(); |         int16_t intRes = readResponseInt(); | ||||||
|         exitCommand(); |         XBEE_COMMAND_END_DECORATOR | ||||||
|         switch(intRes) { |         switch(intRes) { | ||||||
|           case 0x00:  // 0x00 = The socket is definitely open |           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 |           case 0xFF:  // 0xFF = No known status - this is always returned prior to sending data | ||||||
|             return true; |             return true; | ||||||
|           case 0x02:  // 0x02 = Invalid parameters (bad IP/host) |           case 0x02:  // 0x02 = Invalid parameters (bad IP/host) | ||||||
| @@ -901,19 +971,13 @@ public: | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   template<typename... Args> | TINY_GSM_MODEM_STREAM_UTILITIES() | ||||||
|   void sendAT(Args... cmd) { |  | ||||||
|     streamWrite("AT", cmd..., GSM_NL); |  | ||||||
|     stream.flush(); |  | ||||||
|     TINY_GSM_YIELD(); |  | ||||||
|     //DBG("### AT:", cmd...); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // TODO: Optimize this! |   // TODO: Optimize this! | ||||||
|   // NOTE:  This function is used while INSIDE command mode, so we're only |   // NOTE:  This function is used while INSIDE command mode, so we're only | ||||||
|   // waiting for requested responses.  The XBee has no unsoliliced responses |   // waiting for requested responses.  The XBee has no unsoliliced responses | ||||||
|   // (URC's) when in command mode. |   // (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 r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) |                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||||
|   { |   { | ||||||
| @@ -949,7 +1013,7 @@ public: | |||||||
|           goto finish; |           goto finish; | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } while (millis() - startMillis < timeout); |     } while (millis() - startMillis < timeout_ms); | ||||||
| finish: | finish: | ||||||
|     if (!index) { |     if (!index) { | ||||||
|       data.trim(); |       data.trim(); | ||||||
| @@ -971,12 +1035,12 @@ finish: | |||||||
|     return index; |     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 r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), | ||||||
|                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) |                        GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) | ||||||
|   { |   { | ||||||
|     String data; |     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), |   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); |     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 triesMade = 0; | ||||||
|     uint8_t triesUntilReset = 2;  // only reset after 2 failures |     uint8_t triesUntilReset = 4;  // only reset after 4 failures | ||||||
|     bool success = false; |     bool success = false; | ||||||
|     streamClear();  // Empty everything in the buffer before starting |     streamClear();  // Empty everything in the buffer before starting | ||||||
|  |  | ||||||
|     while (!success and triesMade < retries) { |     while (!success and triesMade < retries) { | ||||||
|       // Cannot send anything for 1 "guard time" before entering command mode |       // 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 |       // 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 |       streamWrite(GF("+++"));  // enter command mode | ||||||
|       int res = waitResponse(guardTime*2); |       int res = waitResponse(guardTime*2); | ||||||
|       success = (1 == res); |       success = (1 == res); | ||||||
|       if (0 == res) { |       if (0 == res) { | ||||||
|         triesUntilReset--; |         triesUntilReset--; | ||||||
|         if (triesUntilReset == 0) { |         if (triesUntilReset == 0) { | ||||||
|           triesUntilReset = 2; |           triesUntilReset = 4; | ||||||
|           pinReset();  // if it's unresponsive, reset |           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 ++; |       triesMade ++; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (success) { | ||||||
|  |       inCommandMode = true; | ||||||
|  |       lastCommandModeMillis = millis(); | ||||||
|  |     } | ||||||
|     return success; |     return success; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -1019,8 +1094,11 @@ finish: | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   void exitCommand(void) { |   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 |     sendAT(GF("CN"));  // Exit command mode | ||||||
|     waitResponse(); |     waitResponse(); | ||||||
|  |     inCommandMode = false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool exitAndFail(void) { |   bool exitAndFail(void) { | ||||||
| @@ -1035,23 +1113,37 @@ finish: | |||||||
|     DBG(GF("### Modem: "), getModemName()); |     DBG(GF("### Modem: "), getModemName()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   String readResponseString(uint32_t timeout = 1000) { |   String readResponseString(uint32_t timeout_ms = 1000) { | ||||||
|     TINY_GSM_YIELD(); |     TINY_GSM_YIELD(); | ||||||
|     unsigned long startMillis = millis(); |     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 |     String res = stream.readStringUntil('\r');  // lines end with carriage returns | ||||||
|     res.trim(); |     res.trim(); | ||||||
|     return res; |     return res; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   int16_t readResponseInt(uint32_t timeout = 1000) { |   int16_t readResponseInt(uint32_t timeout_ms = 1000) { | ||||||
|     String res = readResponseString(timeout);  // it just works better reading a string first |     String res = readResponseString(timeout_ms);  // it just works better reading a string first | ||||||
|  |     if (res == "") res = "FF"; | ||||||
|     char buf[5] = {0,}; |     char buf[5] = {0,}; | ||||||
|     res.toCharArray(buf, 5); |     res.toCharArray(buf, 5); | ||||||
|     int16_t intRes = strtol(buf, 0, 16); |     int16_t intRes = strtol(buf, 0, 16); | ||||||
|     return intRes; |     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: | public: | ||||||
|   Stream&       stream; |   Stream&       stream; | ||||||
|  |  | ||||||
| @@ -1061,6 +1153,8 @@ protected: | |||||||
|   XBeeType      beeType; |   XBeeType      beeType; | ||||||
|   IPAddress     savedIP; |   IPAddress     savedIP; | ||||||
|   String        savedHost; |   String        savedHost; | ||||||
|  |   bool          inCommandMode; | ||||||
|  |   uint32_t      lastCommandModeMillis; | ||||||
|   GsmClient*    sockets[TINY_GSM_MUX_COUNT]; |   GsmClient*    sockets[TINY_GSM_MUX_COUNT]; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ | |||||||
| #define TinyGsmCommon_h | #define TinyGsmCommon_h | ||||||
|  |  | ||||||
| // The current library version number | // The current library version number | ||||||
| #define TINYGSM_VERSION "0.6.2" | #define TINYGSM_VERSION "0.7.4" | ||||||
|  |  | ||||||
| #if defined(SPARK) || defined(PARTICLE) | #if defined(SPARK) || defined(PARTICLE) | ||||||
|   #include "Particle.h" |   #include "Particle.h" | ||||||
| @@ -34,10 +34,6 @@ | |||||||
|   #define TINY_GSM_YIELD() { delay(0); } |   #define TINY_GSM_YIELD() { delay(0); } | ||||||
| #endif | #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_AVAILABLE __attribute__((error("Not available on this modem type"))) | ||||||
| #define TINY_GSM_ATTR_NOT_IMPLEMENTED __attribute__((error("Not implemented"))) | #define TINY_GSM_ATTR_NOT_IMPLEMENTED __attribute__((error("Not implemented"))) | ||||||
|  |  | ||||||
| @@ -203,136 +199,425 @@ String TinyGsmDecodeHex16bit(String &instr) { | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Connect to a IP address given as an IPAddress object by | ||||||
|  | // converting said IP address to text | ||||||
| class TinyGsmModem | #define TINY_GSM_CLIENT_CONNECT_OVERLOADS() \ | ||||||
| { |   virtual int connect(IPAddress ip, uint16_t port, int timeout_s) { \ | ||||||
|  |     String host; host.reserve(16); \ | ||||||
| public: |     host += ip[0]; \ | ||||||
|  |     host += "."; \ | ||||||
|   TinyGsmModem(Stream& stream) |     host += ip[1]; \ | ||||||
|     : stream(stream) |     host += "."; \ | ||||||
|   {} |     host += ip[2]; \ | ||||||
|  |     host += "."; \ | ||||||
|   /* |     host += ip[3]; \ | ||||||
|    * Basic functions |     return connect(host.c_str(), port, timeout_s); \ | ||||||
|    */ |   } \ | ||||||
|  |   virtual int connect(const char *host, uint16_t port) { \ | ||||||
|   // Prepare the modem for further functionality |     return connect(host, port, 75); \ | ||||||
|   virtual bool init(const char* pin = NULL) = 0; |   } \ | ||||||
|   // Begin is redundant with init |   virtual int connect(IPAddress ip, uint16_t port) { \ | ||||||
|   virtual bool begin(const char* pin = NULL) { |     return connect(ip, port, 75); \ | ||||||
|     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; |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   /* |  | ||||||
|    * WiFi functions - only apply to WiFi modems |  | ||||||
|    */ |  | ||||||
|  |  | ||||||
|   virtual bool networkConnect(const char* ssid, const char* pwd) { return false; } | // Writes data out on the client using the modem send functionality | ||||||
|   virtual bool networkDisconnect() { return false; } | #define TINY_GSM_CLIENT_WRITE() \ | ||||||
|  |   virtual size_t write(const uint8_t *buf, size_t size) { \ | ||||||
|   /* |     TINY_GSM_YIELD(); \ | ||||||
|    * GPRS functions - only apply to cellular modems |     at->maintain(); \ | ||||||
|    */ |     return at->modemSend(buf, size, mux); \ | ||||||
|  |   } \ | ||||||
|   virtual bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { |   \ | ||||||
|     return false; |   virtual size_t write(uint8_t c) {\ | ||||||
|   } |     return write(&c, 1); \ | ||||||
|   virtual bool gprsDisconnect() { return false; } |   }\ | ||||||
|   virtual bool isGprsConnected() { return false; } |   \ | ||||||
|  |   virtual size_t write(const char *str) { \ | ||||||
|   /* |     if (str == NULL) return 0; \ | ||||||
|    * IP Address functions |     return write((const uint8_t *)str, strlen(str)); \ | ||||||
|    */ |  | ||||||
|  |  | ||||||
|   virtual String getLocalIP() = 0; |  | ||||||
|   virtual IPAddress localIP() { |  | ||||||
|     return TinyGsmIpFromString(getLocalIP()); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|     /* |  | ||||||
|      Utilities |  | ||||||
|      */ |  | ||||||
|  |  | ||||||
|     template<typename T> | // Returns the combined number of characters available in the TinyGSM fifo | ||||||
|     void streamWrite(T last) { | // and the modem chips internal fifo, doing an extra check-in with the | ||||||
|       stream.print(last); | // modem to see if anything has arrived without a UURC. | ||||||
|     } | #define TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK() \ | ||||||
|  |   virtual int available() { \ | ||||||
|     template<typename T, typename... Args> |     TINY_GSM_YIELD(); \ | ||||||
|     void streamWrite(T head, Args... tail) { |     if (!rx.size()) { \ | ||||||
|       stream.print(head); |       /* Workaround: sometimes module forgets to notify about data arrival. | ||||||
|       streamWrite(tail...); |       TODO: Currently we ping the module periodically, | ||||||
|     } |       but maybe there's a better indicator that we need to poll */ \ | ||||||
|  |       if (millis() - prev_check > 250) { \ | ||||||
|     bool streamSkipUntil(const char c, const unsigned long timeout = 1000L) { |         got_data = true; \ | ||||||
|       unsigned long startMillis = millis(); |         prev_check = millis(); \ | ||||||
|       while (millis() - startMillis < timeout) { |       } \ | ||||||
|         while (millis() - startMillis < timeout && !stream.available()) { |       at->maintain(); \ | ||||||
|           TINY_GSM_YIELD(); |     } \ | ||||||
|         } |     return rx.size() + sock_available; \ | ||||||
|         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.  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 | #endif | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user