├── .gitignore ├── .travis.yml ├── .vscode └── c_cpp_properties.json ├── LICENSE.md ├── README.md ├── examples ├── AcquireGPSPosition │ └── AcquireGPSPosition.ino ├── GeneralInformation │ └── GeneralInformation.ino ├── HttpPost │ └── HttpPost.ino └── Tester │ └── Tester.ino ├── library.properties └── src ├── SIM808.Gprs.cpp ├── SIM808.Gps.cpp ├── SIM808.Gsm.cpp ├── SIM808.Http.cpp ├── SIM808.Power.cpp ├── SIM808.Types.h ├── SIM808.cpp ├── SIM808.h ├── SIMComAT.Common.h ├── SIMComAT.cpp └── SIMComAT.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Build results 2 | [Bb]in/ 3 | 4 | #Visual Studio code 5 | .vscode/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | env: 3 | global: 4 | - ARDUINO_IDE_VERSION="1.8.5" 5 | - ARDUINO_ESP32_VERSION="1.0.1" 6 | - LIBRARY_NAME="SIM808" 7 | matrix: 8 | - ESP32=false BOARD="arduino:avr:uno" 9 | - ESP32=true BOARD="esp32:esp32:pico32" 10 | before_install: 11 | - "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16" 12 | - sleep 3 13 | - export DISPLAY=:1.0 14 | - wget http://downloads.arduino.cc/arduino-$ARDUINO_IDE_VERSION-linux64.tar.xz 15 | - tar xf arduino-$ARDUINO_IDE_VERSION-linux64.tar.xz 16 | - sudo mv arduino-$ARDUINO_IDE_VERSION /usr/local/share/arduino 17 | - sudo ln -s /usr/local/share/arduino/arduino /usr/local/bin/arduino 18 | - arduino --pref boardsmanager.additional.urls=https://dl.espressif.com/dl/package_esp32_index.json --save-prefs 19 | - if $ESP32; then arduino --install-boards esp32:esp32:$ARDUINO_ESP32_VERSION; fi 20 | install: 21 | - ln -s $PWD /usr/local/share/arduino/libraries/$LIBRARY_NAME 22 | - arduino --install-library "ArduinoLog" 23 | script: 24 | - arduino --verify --board $BOARD $PWD/examples/AcquireGPSPosition/AcquireGPSPosition.ino 25 | - arduino --verify --board $BOARD $PWD/examples/GeneralInformation/GeneralInformation.ino 26 | - arduino --verify --board $BOARD $PWD/examples/HttpPost/HttpPost.ino 27 | - arduino --verify --board $BOARD $PWD/examples/Tester/Tester.ino 28 | notifications: 29 | email: 30 | on_success: change 31 | on_failure: change -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Win32", 5 | "includePath": [ 6 | "${workspaceFolder}/src/**", 7 | "${workspaceFolder}/../libraries/ArduinoLog", 8 | "${config:arduino.path}/tools/**", 9 | "${config:arduino.path}/hardware/arduino/avr/**", 10 | "${config:arduino.path}/hardware/tools/avr/avr/include/**" 11 | ], 12 | "intelliSenseMode": "clang-x64", 13 | "cStandard": "c11", 14 | "cppStandard": "c++11" 15 | } 16 | ], 17 | "version": 4 18 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Bertrand Lemasle 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SIM808 2 | [![Build Status](https://travis-ci.org/blemasle/arduino-sim808.svg?branch=master)](https://travis-ci.org/blemasle/arduino-sim808) 3 | [![License](https://img.shields.io/badge/license-MIT%20License-blue.svg)](http://doge.mit-license.org) 4 | 5 | This library allows to access some of the features of the [SIM808](https://simcom.ee/documents/?dir=SIM808) GPS & GPRS module. It requires only the `RESET` pin to work and a TTL Serial. `STATUS` pin can be wired to enhance the module power status detection, while wiring the `PWRKEY` adds the ability to turn the module on & off. 6 | 7 | The library tries to reduces memory consumption as much as possible, but nonetheless use a 64 bytes buffer to communicate with the SIM808 module. When available, SIM808 responses are parsed to ensure that commands are correctly executed by the module. Commands timeouts are also set according to SIMCOM documentation. 8 | 9 | > No default instance is created when the library is included 10 | 11 | [Arduino-Log](https://github.com/thijse/Arduino-Log) is used to output formatted commands in a `printf` style. This make implementation of new commands 12 | really easy, and avoid successive prints or string concatenation on complex commands. 13 | 14 | ## Features 15 | * Fine control over the module power management 16 | * Sending SMS 17 | * Sending GET and POST [HTTP(s)](#a-note-about-https) requests 18 | * Acquiring GPS positions, with access to individual fields 19 | * Reading of the device states (battery, gps, network) 20 | 21 | ## Why another library ? 22 | There is a number of libraries out there which support this modem ([Adafruit's FONA](https://github.com/adafruit/Adafruit_FONA), [TinyGSM](https://github.com/vshymanskyy/TinyGSM) for instance), so why build another one ? None fit the needs I had for a project. FONA is more a giant example for testing commands individually and I was getting unreliable results with it. TinyGSM seems great but what it gains in chips support it lacks in fine grained control over each modules, which I needed. 23 | 24 | This library is then greatly inspired by FONA, which served as the reference implementation, but mostly only support the features I needed for my project and has been tested thoroughly and successfully in that configuration. It also tries to reduce the final HEX size as this was a real problem for the project it was built for. 25 | 26 | It does *not* have the pretention to become the new SIM808 standard library, but can be useful to others as a source of inspiration or documentation to understand how AT commands works. 27 | 28 | ## Debugging 29 | If you need to debug the communication with the SIM808 module, you can either define `_DEBUG` to `1`, or directly change `_SIM808_DEBUG` to `1` in [SIMComAT.h](/src/SIMComAT.h). 30 | > Be aware that it will increase the final hex size as debug strings are stored in flash. 31 | 32 | ## Usage 33 | No default instance is created when the library is included. It's up to you to create one with the appropriate parameters. 34 | 35 | ```cpp 36 | #include 37 | #include 38 | 39 | #define SIM_RST 5 ///< SIM808 RESET 40 | #define SIM_RX 6 ///< SIM808 RXD 41 | #define SIM_TX 7 ///< SIM808 TXD 42 | #define SIM_PWR 9 ///< SIM808 PWRKEY 43 | #define SIM_STATUS 8 ///< SIM808 STATUS 44 | 45 | #define SIM808_BAUDRATE 4800 ///< Control the baudrate use to communicate with the SIM808 module 46 | 47 | SoftwareSerial simSerial = SoftwareSerial(SIM_TX, SIM_RX); 48 | SIM808 sim808 = SIM808(SIM_RST, SIM_PWR, SIM_STATUS); 49 | // SIM808 sim808 = SIM808(SIM_RST); // if you only have the RESET pin wired 50 | // SIM808 sim808 = SIM808(SIM_RST, SIM_PWR); // if you only have the RESET and PWRKEY pins wired 51 | 52 | void setup() { 53 | simSerial.begin(SIM808_BAUDRATE); 54 | sim808.begin(simSerial); 55 | 56 | sim808.powerOnOff(true); //power on the SIM808. Unavailable without the PWRKEY pin wired 57 | sim808.init(); 58 | } 59 | 60 | void loop() { 61 | // whatever you need to do 62 | } 63 | ``` 64 | See examples for further usage. 65 | 66 | ## A note about HTTPS 67 | 68 | While technically, SIM808 module support HTTPS requests through the HTTP service, it is particularly unreliable and sketchy. 7 times out of 10, the request won't succeed. 69 | In the future, I hope to find the time to make HTTPS work with the TCP service. In the meantime I strongly (and sadly) recommend to stick with HTTP requests if you need reliability. -------------------------------------------------------------------------------- /examples/AcquireGPSPosition/AcquireGPSPosition.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Acquire a GPS position and display it on Serial 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #if defined(__AVR__) 9 | #include 10 | #define SIM_SERIAL_TYPE SoftwareSerial ///< Type of variable that holds the Serial communication with SIM808 11 | #define SIM_SERIAL SIM_SERIAL_TYPE(SIM_TX, SIM_RX) ///< Definition of the instance that holds the Serial communication with SIM808 12 | #else 13 | #include 14 | #define SIM_SERIAL_TYPE HardwareSerial ///< Type of variable that holds the Serial communication with SIM808 15 | #define SIM_SERIAL SIM_SERIAL_TYPE(2) ///< Definition of the instance that holds the Serial communication with SIM808 16 | #endif 17 | 18 | #define SIM_RST 5 ///< SIM808 RESET 19 | #define SIM_RX 6 ///< SIM808 RXD 20 | #define SIM_TX 7 ///< SIM808 TXD 21 | #define SIM_PWR 9 ///< SIM808 PWRKEY 22 | #define SIM_STATUS 8 ///< SIM808 STATUS 23 | 24 | #define SIM808_BAUDRATE 4800 ///< Control the baudrate use to communicate with the SIM808 module 25 | #define SERIAL_BAUDRATE 38400 ///< Controls the serial baudrate between the arduino and the computer 26 | #define NO_FIX_GPS_DELAY 3000 ///< Delay between each GPS read when no fix is acquired 27 | #define FIX_GPS_DELAY 10000 ///< Delay between each GPS read when a fix is acquired 28 | 29 | #define POSITION_SIZE 128 ///< Size of the position buffer 30 | #define NL "\n" 31 | 32 | #if defined(__AVR__) 33 | typedef __FlashStringHelper* __StrPtr; 34 | #else 35 | typedef const char* __StrPtr; 36 | #endif 37 | 38 | SIM_SERIAL_TYPE simSerial = SIM_SERIAL; 39 | SIM808 sim808 = SIM808(SIM_RST, SIM_PWR, SIM_STATUS); 40 | char position[POSITION_SIZE]; 41 | 42 | void setup() { 43 | Serial.begin(SERIAL_BAUDRATE); 44 | Log.begin(LOG_LEVEL_NOTICE, &Serial); 45 | 46 | simSerial.begin(SIM808_BAUDRATE); 47 | sim808.begin(simSerial); 48 | 49 | Log.notice(S_F("Powering on SIM808..." NL)); 50 | sim808.powerOnOff(true); 51 | sim808.init(); 52 | 53 | Log.notice(S_F("Powering on SIM808's GPS..." NL)); 54 | sim808.powerOnOffGps(true); 55 | } 56 | 57 | void loop() { 58 | SIM808GpsStatus status = sim808.getGpsStatus(position, POSITION_SIZE); 59 | 60 | if(status < SIM808GpsStatus::Fix) { 61 | Log.notice(S_F("No fix yet..." NL)); 62 | delay(NO_FIX_GPS_DELAY); 63 | return; 64 | } 65 | 66 | uint16_t sattelites; 67 | float lat, lon; 68 | __StrPtr state; 69 | 70 | if(status == SIM808GpsStatus::Fix) state = S_F("Normal"); 71 | else state = S_F("Accurate"); 72 | 73 | sim808.getGpsField(position, SIM808GpsField::GnssUsed, &sattelites); 74 | sim808.getGpsField(position, SIM808GpsField::Latitude, &lat); 75 | sim808.getGpsField(position, SIM808GpsField::Longitude, &lon); 76 | 77 | Log.notice(S_F("%s" NL), position); 78 | Log.notice(S_F("Fix type: %S" NL), state); 79 | Log.notice(S_F("Sattelites used : %d" NL), sattelites); 80 | Log.notice(S_F("Latitude : %F" NL), lat); 81 | Log.notice(S_F("Longitude : %F" NL), lon); 82 | 83 | delay(FIX_GPS_DELAY); 84 | } -------------------------------------------------------------------------------- /examples/GeneralInformation/GeneralInformation.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Display general information about the SIM808 chip on Serial 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #if defined(__AVR__) 9 | #include 10 | #define SIM_SERIAL_TYPE SoftwareSerial ///< Type of variable that holds the Serial communication with SIM808 11 | #define SIM_SERIAL SIM_SERIAL_TYPE(SIM_TX, SIM_RX) ///< Definition of the instance that holds the Serial communication with SIM808 12 | 13 | #define STRLCPY_P(s1, s2) strlcpy_P(s1, s2, BUFFER_SIZE) 14 | #else 15 | #include 16 | #define SIM_SERIAL_TYPE HardwareSerial ///< Type of variable that holds the Serial communication with SIM808 17 | #define SIM_SERIAL SIM_SERIAL_TYPE(2) ///< Definition of the instance that holds the Serial communication with SIM808 18 | 19 | #define STRLCPY_P(s1, s2) strlcpy(s1, s2, BUFFER_SIZE) 20 | #endif 21 | 22 | #define SIM_RST 5 ///< SIM808 RESET 23 | #define SIM_RX 6 ///< SIM808 RXD 24 | #define SIM_TX 7 ///< SIM808 TXD 25 | #define SIM_PWR 9 ///< SIM808 PWRKEY 26 | #define SIM_STATUS 8 ///< SIM808 STATUS 27 | 28 | #define SIM808_BAUDRATE 4800 ///< Control the baudrate use to communicate with the SIM808 module 29 | #define SERIAL_BAUDRATE 38400 ///< Controls the serial baudrate between the arduino and the computer 30 | #define NETWORK_DELAY 10000 ///< Delay between each GPS read 31 | 32 | #define BUFFER_SIZE 512 ///< Size of the buffer 33 | #define NL "\n" 34 | 35 | SIM_SERIAL_TYPE simSerial = SIM_SERIAL; 36 | SIM808 sim808 = SIM808(SIM_RST, SIM_PWR, SIM_STATUS); 37 | bool done = false; 38 | char buffer[BUFFER_SIZE]; 39 | 40 | void setup() { 41 | Serial.begin(SERIAL_BAUDRATE); 42 | Log.begin(LOG_LEVEL_NOTICE, &Serial); 43 | 44 | simSerial.begin(SIM808_BAUDRATE); 45 | sim808.begin(simSerial); 46 | 47 | Log.notice(S_F("Powering on SIM808..." NL)); 48 | sim808.powerOnOff(true); 49 | sim808.init(); 50 | 51 | sim808.getImei(buffer, BUFFER_SIZE); 52 | Log.notice(S_F("IMEI : \"%s\"" NL), buffer); 53 | 54 | sim808.getSimState(buffer, BUFFER_SIZE); 55 | Log.notice(S_F("SIM card state : \"%s\"" NL), buffer); 56 | 57 | SIM808ChargingStatus charging = sim808.getChargingState(); 58 | switch(charging.state) { 59 | case SIM808ChargingState::Charging: 60 | STRLCPY_P(buffer, PSTR("Charging")); 61 | break; 62 | case SIM808ChargingState::ChargingDone: 63 | STRLCPY_P(buffer, PSTR("ChargingDone")); 64 | break; 65 | case SIM808ChargingState::Error: 66 | STRLCPY_P(buffer, PSTR("Error")); 67 | break; 68 | case SIM808ChargingState::NotCharging: 69 | STRLCPY_P(buffer, PSTR("NotCharging")); 70 | break; 71 | } 72 | Log.notice(S_F("Charging state : %s, %d%% @ %dmV" NL), buffer, charging.level, charging.voltage); 73 | 74 | //you can also send unimplemented simple commands 75 | sim808.sendCommand("I", buffer, BUFFER_SIZE); 76 | Log.notice(S_F("ATI response : \"%s\"" NL), buffer); 77 | } 78 | 79 | void loop() { } -------------------------------------------------------------------------------- /examples/HttpPost/HttpPost.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Send an HTTP POST request and display the response on Serial 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #if defined(__AVR__) 9 | #include 10 | #define SIM_SERIAL_TYPE SoftwareSerial ///< Type of variable that holds the Serial communication with SIM808 11 | #define SIM_SERIAL SIM_SERIAL_TYPE(SIM_TX, SIM_RX) ///< Definition of the instance that holds the Serial communication with SIM808 12 | 13 | #define STRLCPY_P(s1, s2) strlcpy_P(s1, s2, BUFFER_SIZE) 14 | #else 15 | #include 16 | #define SIM_SERIAL_TYPE HardwareSerial ///< Type of variable that holds the Serial communication with SIM808 17 | #define SIM_SERIAL SIM_SERIAL_TYPE(2) ///< Definition of the instance that holds the Serial communication with SIM808 18 | 19 | #define STRLCPY_P(s1, s2) strlcpy(s1, s2, BUFFER_SIZE) 20 | #endif 21 | 22 | #define SIM_RST 5 ///< SIM808 RESET 23 | #define SIM_RX 6 ///< SIM808 RXD 24 | #define SIM_TX 7 ///< SIM808 TXD 25 | #define SIM_PWR 9 ///< SIM808 PWRKEY 26 | #define SIM_STATUS 8 ///< SIM808 STATUS 27 | 28 | #define SIM808_BAUDRATE 4800 ///< Control the baudrate use to communicate with the SIM808 module 29 | #define SERIAL_BAUDRATE 38400 ///< Controls the serial baudrate between the arduino and the computer 30 | #define NETWORK_DELAY 10000 ///< Delay between each GPS read 31 | 32 | #define GPRS_APN "vodafone" ///< Your provider Access Point Name 33 | #define GPRS_USER NULL ///< Your provider APN user (usually not needed) 34 | #define GPRS_PASS NULL ///< Your provider APN password (usually not needed) 35 | 36 | #define BUFFER_SIZE 512 ///< Side of the response buffer 37 | #define NL "\n" 38 | 39 | SIM_SERIAL_TYPE simSerial = SIM_SERIAL; 40 | SIM808 sim808 = SIM808(SIM_RST, SIM_PWR, SIM_STATUS); 41 | bool done = false; 42 | char buffer[BUFFER_SIZE]; 43 | 44 | void setup() { 45 | Serial.begin(SERIAL_BAUDRATE); 46 | Log.begin(LOG_LEVEL_NOTICE, &Serial); 47 | 48 | simSerial.begin(SIM808_BAUDRATE); 49 | sim808.begin(simSerial); 50 | 51 | Log.notice(S_F("Powering on SIM808..." NL)); 52 | sim808.powerOnOff(true); 53 | sim808.init(); 54 | } 55 | 56 | void loop() { 57 | if(done) { 58 | delay(NETWORK_DELAY); 59 | return; 60 | } 61 | 62 | SIM808NetworkRegistrationState status = sim808.getNetworkRegistrationStatus(); 63 | SIM808SignalQualityReport report = sim808.getSignalQuality(); 64 | 65 | bool isAvailable = static_cast(status) & 66 | (static_cast(SIM808NetworkRegistrationState::Registered) | static_cast(SIM808NetworkRegistrationState::Roaming)) 67 | != 0; 68 | 69 | if(!isAvailable) { 70 | Log.notice(S_F("No network yet..." NL)); 71 | delay(NETWORK_DELAY); 72 | return; 73 | } 74 | 75 | Log.notice(S_F("Network is ready." NL)); 76 | Log.notice(S_F("Attenuation : %d dBm, Estimated quality : %d" NL), report.attenuation, report.rssi); 77 | 78 | bool enabled = false; 79 | do { 80 | Log.notice(S_F("Powering on SIM808's GPRS..." NL)); 81 | enabled = sim808.enableGprs(GPRS_APN, GPRS_USER, GPRS_PASS); 82 | } while(!enabled); 83 | 84 | Log.notice(S_F("Sending HTTP request..." NL)); 85 | STRLCPY_P(buffer, PSTR("This is the body")); 86 | //notice that we're using the same buffer for both body and response 87 | uint16_t responseCode = sim808.httpPost("http://httpbin.org/anything", S_F("text/plain"), buffer, buffer, BUFFER_SIZE); 88 | 89 | Log.notice(S_F("Server responsed : %d" NL), responseCode); 90 | Log.notice(buffer); 91 | 92 | done = true; 93 | } -------------------------------------------------------------------------------- /examples/Tester/Tester.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Provides a serial interface to test functions of the SIM808 library 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #if defined(__AVR__) 9 | #include 10 | #define SIM_SERIAL_TYPE SoftwareSerial ///< Type of variable that holds the Serial communication with SIM808 11 | #define SIM_SERIAL SIM_SERIAL_TYPE(SIM_TX, SIM_RX) ///< Definition of the instance that holds the Serial communication with SIM808 12 | #else 13 | #include 14 | #define SIM_SERIAL_TYPE HardwareSerial ///< Type of variable that holds the Serial communication with SIM808 15 | #define SIM_SERIAL SIM_SERIAL_TYPE(2) ///< Definition of the instance that holds the Serial communication with SIM808 16 | #endif 17 | 18 | #define SIM_RST 5 ///< SIM808 RESET 19 | #define SIM_RX 6 ///< SIM808 RXD 20 | #define SIM_TX 7 ///< SIM808 TXD 21 | #define SIM_PWR 9 ///< SIM808 PWRKEY 22 | #define SIM_STATUS 8 ///< SIM808 STATUS 23 | 24 | #define SIM808_BAUDRATE 4800 ///< Controls the serial baudrate between the arduino and the SIM808 module 25 | #define SERIAL_BAUDRATE 38400 ///< Controls the serial baudrate between the arduino and the computer 26 | 27 | #define BUFFER_SIZE 512 ///< Size of the buffer on the main program side 28 | #define NL "\n" 29 | 30 | #if defined(__AVR__) 31 | typedef __FlashStringHelper* __StrPtr; 32 | 33 | #define STRING_IS(s1, s2) strcasecmp_P(s1, PSTR(s2)) == 0 34 | #define BUFFER_IS(s) STRING_IS(buffer, s) 35 | #define BUFFER_IS_P(s) strcasecmp_P(buffer, s) == 0 36 | 37 | #define PRINT(s) Serial.print(S_F(s)) 38 | #define PRINT_LN(s) Serial.println(S_F(s)) 39 | 40 | #define STRLCPY_P(s1, s2, size) strlcpy_P(s1, s2, size) 41 | #else 42 | typedef const char* __StrPtr; 43 | 44 | #define STRING_IS(s1, s2) strcasecmp(s1, s2) == 0 45 | #define BUFFER_IS(s) STRING_IS(buffer, s) 46 | #define BUFFER_IS_P(s) strcasecmp(buffer, s) == 0 47 | 48 | #define PRINT(s) Serial.print(s) 49 | #define PRINT_LN(s) Serial.println(s) 50 | 51 | #define STRLCPY_P(s1, s2, size) strlcpy(s1, s2, size) 52 | #endif 53 | 54 | namespace strings { 55 | const char UNRECOGNIZED[] S_PROGMEM = "Unrecognized : %s" NL; 56 | const char UNKNOWN[] S_PROGMEM = "Unknown value"; 57 | 58 | const char YES[] S_PROGMEM = "Yes"; 59 | const char NO[] S_PROGMEM = "No"; 60 | 61 | const char SUCCESS[] S_PROGMEM = "Success"; 62 | const char FAILED[] S_PROGMEM = "Failed"; 63 | const char ERROR[] S_PROGMEM = "Error"; 64 | 65 | const char ON[] S_PROGMEM = "On"; 66 | const char OFF[] S_PROGMEM = "Off"; 67 | const char STATUS[] S_PROGMEM = "Status"; 68 | const char ALL[] S_PROGMEM = "All"; 69 | 70 | const char MAIN[] S_PROGMEM = "Main"; 71 | const char GPS[] S_PROGMEM = "Gps"; 72 | const char NETWORK[] S_PROGMEM = "Network"; 73 | 74 | const char MINIMUM[] S_PROGMEM = "Minimum"; 75 | const char FULL[] S_PROGMEM = "Full"; 76 | const char RF_DISABLED[] S_PROGMEM = "Disabled"; 77 | 78 | const char SEND[] S_PROGMEM = "Send"; 79 | const char DEFAULT_URL[] S_PROGMEM = "http://httpbin.org/anything"; 80 | }; 81 | 82 | SIM_SERIAL_TYPE simSerial = SIM_SERIAL; 83 | SIM808 sim808 = SIM808(SIM_RST, SIM_PWR, SIM_STATUS); 84 | char buffer[BUFFER_SIZE]; 85 | 86 | void usage() { 87 | PRINT_LN("============= SIM808 Tester ============="); 88 | PRINT_LN("?\tHelp"); 89 | 90 | PRINT_LN(""); 91 | 92 | PRINT_LN("reset\t\t\t\tReset the device"); 93 | PRINT_LN("init\t\t\t\tInit the device"); 94 | PRINT_LN("power [on|off] [main|gps|network]\tPower On/Off the specified module"); 95 | PRINT_LN("power status [main|gps|network]\t\tGet the specified module power state"); 96 | 97 | PRINT_LN(""); 98 | 99 | PRINT_LN("charge\tGet the current charge state"); 100 | 101 | PRINT_LN(""); 102 | 103 | PRINT_LN("sim status\t\tGet the current SIM state"); 104 | PRINT_LN("sim unlock [pin]\tUnlock the SIM using the provided pin"); 105 | PRINT_LN("imei\t\t\tGet the device IMEI"); 106 | 107 | PRINT_LN(""); 108 | 109 | PRINT_LN("network functionality\t\t\t\tGet the current network functionality"); 110 | PRINT_LN("network functionality [minimum|full|disabled]\tSet the current network functionality"); 111 | PRINT_LN("network status\t\t\t\t\tGet the current network status"); 112 | PRINT_LN("network quality\t\t\t\t\tGet the current network quality report"); 113 | 114 | PRINT_LN(""); 115 | 116 | PRINT_LN("gps raw\t\t\tGet the GPS raw data"); 117 | PRINT_LN("gps parse [field]\tParse GPS fields, field :"); 118 | PRINT_LN("\tall\t- All fields"); 119 | PRINT_LN("\ttime\t- UTC time"); 120 | PRINT_LN("\tlat\t- Latitude"); 121 | PRINT_LN("\tlon\t- Longitude"); 122 | PRINT_LN("\talt\t- Altitude"); 123 | PRINT_LN("\tspeed\t- Speed over ground"); 124 | PRINT_LN("\tcourse\t- Course over ground"); 125 | PRINT_LN("\tsatview\t- Satellites in view"); 126 | PRINT_LN("\tsatused\t- Satellites used"); 127 | 128 | PRINT_LN(""); 129 | 130 | PRINT_LN("send at [command]\t\t\t\t\t\tSend an AT command"); 131 | PRINT_LN("send sms [number]\t\t\t\t\t\tSend an SMS"); 132 | PRINT_LN("send http [get|post] [url?=http://httpbin.org/anything]\t\tSend an HTTP request"); 133 | 134 | PRINT_LN(""); 135 | PRINT_LN(""); 136 | } 137 | 138 | /** 139 | * Read the next line or word into the buffer. 140 | * Returns true if this was the last word of the line. 141 | */ 142 | bool readNext(bool line = false) { 143 | char c; 144 | char *p = buffer; 145 | memset(p, 0, BUFFER_SIZE); 146 | 147 | do { 148 | while(!Serial.available()) delay(100); 149 | 150 | c = Serial.read(); 151 | if((!line && isspace(c)) || c == '\n') break; 152 | 153 | *p = c; 154 | p++; 155 | } while(true); 156 | 157 | *p = '\0'; 158 | return c == '\n'; 159 | } 160 | 161 | void unrecognized() { 162 | Log.error(TO_F(strings::UNRECOGNIZED), buffer); 163 | while(Serial.available()) Serial.read(); 164 | } 165 | 166 | void reset() { 167 | Log.notice(S_F("Resetting SIM808..." NL)); 168 | sim808.reset(); 169 | } 170 | 171 | void init_() { 172 | Log.notice(S_F("Init SIM808..." NL)); 173 | sim808.init(); 174 | } 175 | 176 | void power() { 177 | readNext(); 178 | 179 | if(BUFFER_IS_P(strings::STATUS)) { 180 | bool status; 181 | __StrPtr statusText; 182 | readNext(); 183 | 184 | if(BUFFER_IS_P(strings::MAIN)) status = sim808.powered(); 185 | else if (BUFFER_IS_P(strings::GPS)) sim808.getGpsPowerState(&status); 186 | else if (BUFFER_IS_P(strings::NETWORK)) sim808.getGprsPowerState(&status); 187 | else { 188 | unrecognized(); 189 | return; 190 | } 191 | 192 | Log.notice(S_F("%s : %S" NL), buffer, status ? TO_F(strings::ON) : TO_F(strings::OFF)); 193 | } 194 | else if(BUFFER_IS_P(strings::ON)) { 195 | readNext(); 196 | 197 | if(BUFFER_IS_P(strings::MAIN)) { 198 | bool poweredOn = sim808.powerOnOff(true); 199 | Log.notice(S_F("powered on %S : %S" NL), TO_F(strings::MAIN), poweredOn ? TO_F(strings::YES) : TO_F(strings::NO)); 200 | } 201 | else if(BUFFER_IS_P(strings::GPS)) { 202 | bool poweredOn = sim808.powerOnOffGps(true); 203 | Log.notice(S_F("powered on %S : %S" NL), TO_F(strings::GPS), poweredOn ? TO_F(strings::YES) : TO_F(strings::NO)); 204 | } 205 | else if(BUFFER_IS_P(strings::NETWORK)) { 206 | char apn[15], user[15], pass[15]; 207 | char *userP = NULL, *passP = NULL; 208 | 209 | PRINT("[apn] [pass?=\"\"] [user?=\"\"] ?"); 210 | bool last = readNext(); 211 | strncpy(apn, buffer, 15); 212 | 213 | if(!last) { 214 | last = readNext(); 215 | strncpy(user, buffer, 15); 216 | userP = &user[0]; 217 | } 218 | 219 | if(!last) { 220 | last = readNext(); 221 | strncpy(pass, buffer, 15); 222 | passP = &pass[0]; 223 | } 224 | 225 | bool success = sim808.enableGprs(apn, userP, passP); 226 | Log.notice(S_F("enable GPRS : %S" NL), success ? TO_F(strings::SUCCESS) : TO_F(strings::FAILED)); 227 | } 228 | else { 229 | unrecognized(); 230 | return; 231 | } 232 | } 233 | else if(BUFFER_IS_P(strings::OFF)) { 234 | readNext(); 235 | 236 | if(BUFFER_IS_P(strings::MAIN)) { 237 | bool poweredOff = sim808.powerOnOff(false); 238 | Log.notice(S_F("powered off %S : %S" NL), TO_F(strings::MAIN), poweredOff ? TO_F(strings::YES) : TO_F(strings::NO)); 239 | } 240 | else if(BUFFER_IS_P(strings::GPS)) { 241 | bool poweredOff = sim808.powerOnOffGps(false); 242 | Log.notice(S_F("powered off %S : %S" NL), TO_F(strings::GPS), poweredOff ? TO_F(strings::YES) : TO_F(strings::NO)); 243 | 244 | } 245 | else if(BUFFER_IS_P(strings::NETWORK)) sim808.disableGprs(); 246 | else { 247 | unrecognized(); 248 | return; 249 | } 250 | } 251 | else { 252 | unrecognized(); 253 | return; 254 | } 255 | } 256 | 257 | void charge() { 258 | SIM808ChargingStatus status = sim808.getChargingState(); 259 | __StrPtr state; 260 | 261 | switch(status.state) { 262 | case SIM808ChargingState::Error: 263 | state = TO_F(strings::ERROR); 264 | break; 265 | case SIM808ChargingState::NotCharging: 266 | state = S_F("NotCharging"); 267 | break; 268 | case SIM808ChargingState::Charging: 269 | state = S_F("Charging"); 270 | break; 271 | case SIM808ChargingState::ChargingDone: 272 | state = S_F("ChargingDone"); 273 | break; 274 | default: 275 | state = TO_F(strings::UNKNOWN); 276 | break; 277 | } 278 | 279 | Log.notice(S_F("Charging status" NL)); 280 | Log.notice(S_F("state\t: %S" NL), state); 281 | Log.notice(S_F("level\t: %d%%" NL), status.level); 282 | Log.notice(S_F("voltage\t: %dmV" NL), status.voltage); 283 | } 284 | 285 | void sim() { 286 | readNext(); 287 | 288 | if(BUFFER_IS_P(strings::STATUS)) { 289 | sim808.getSimState(buffer, BUFFER_SIZE); 290 | Log.notice(S_F("SIM status : \"%s\"" NL), buffer); 291 | } 292 | else if(BUFFER_IS("UNLOCK")) { 293 | readNext(); 294 | 295 | size_t length = strlen(buffer); 296 | if(length != 4) { 297 | Log.error(S_F("4 digit pin code required" NL)); 298 | return; 299 | } 300 | 301 | bool success = sim808.simUnlock(buffer); 302 | Log.notice(S_F("SIM unlock : %S" NL), success ? TO_F(strings::SUCCESS) : TO_F(strings::FAILED)); 303 | } 304 | else { 305 | unrecognized(); 306 | return; 307 | } 308 | } 309 | 310 | void imei() { 311 | sim808.getImei(buffer, BUFFER_SIZE); 312 | Log.notice(S_F("IMEI : \"%s\"" NL), buffer); 313 | } 314 | 315 | void network() { 316 | bool last = readNext(); 317 | __StrPtr state; 318 | 319 | if(BUFFER_IS_P(strings::STATUS)) { 320 | SIM808NetworkRegistrationState status = sim808.getNetworkRegistrationStatus(); 321 | 322 | switch(status) { 323 | case SIM808NetworkRegistrationState::Error: 324 | state = TO_F(strings::ERROR); 325 | break; 326 | case SIM808NetworkRegistrationState::NotSearching: 327 | state = S_F("NotSearching"); 328 | break; 329 | case SIM808NetworkRegistrationState::Registered: 330 | state = S_F("Registered"); 331 | break; 332 | case SIM808NetworkRegistrationState::Searching: 333 | state = S_F("Searching"); 334 | break; 335 | case SIM808NetworkRegistrationState::Denied: 336 | state = S_F("Denied"); 337 | break; 338 | case SIM808NetworkRegistrationState::Unknown: 339 | state = S_F("Unknown"); 340 | break; 341 | case SIM808NetworkRegistrationState::Roaming: 342 | state = S_F("Roaming"); 343 | break; 344 | default: 345 | state = TO_F(strings::UNKNOWN); 346 | break; 347 | } 348 | 349 | Log.notice(S_F("status : %S" NL), state); 350 | 351 | } 352 | else if(BUFFER_IS("QUALITY")) { 353 | SIM808SignalQualityReport report = sim808.getSignalQuality(); 354 | 355 | Log.notice(S_F("Signal Quality" NL)); 356 | Log.notice(S_F("signal strength \t: %d" NL), report.rssi); 357 | Log.notice(S_F("bit error rate \t: %d" NL), report.ber); 358 | Log.notice(S_F("attenuation \t\t: %ddBm" NL), report.attenuation); 359 | } 360 | else if(BUFFER_IS("FUNCTIONALITY")) { 361 | if(last) { //get 362 | SIM808PhoneFunctionality fun = sim808.getPhoneFunctionality(); 363 | switch(fun) { 364 | case SIM808PhoneFunctionality::Fail: 365 | state = TO_F(strings::FAILED); 366 | break; 367 | case SIM808PhoneFunctionality::Minimum: 368 | state = TO_F(strings::MINIMUM); 369 | break; 370 | case SIM808PhoneFunctionality::Full: 371 | state = TO_F(strings::FULL); 372 | break; 373 | case SIM808PhoneFunctionality::Disabled: 374 | state = TO_F(strings::RF_DISABLED); 375 | break; 376 | default: 377 | state = TO_F(strings::UNKNOWN); 378 | break; 379 | } 380 | 381 | Log.notice(S_F("phone functionality : %S" NL), state); 382 | } 383 | else { 384 | SIM808PhoneFunctionality fun; 385 | 386 | readNext(); 387 | if(BUFFER_IS_P(strings::MINIMUM)) fun = SIM808PhoneFunctionality::Minimum; 388 | else if(BUFFER_IS_P(strings::FULL)) fun = SIM808PhoneFunctionality::Full; 389 | else if(BUFFER_IS_P(strings::RF_DISABLED)) fun = SIM808PhoneFunctionality::Disabled; 390 | else { 391 | unrecognized(); 392 | return; 393 | } 394 | 395 | bool success = sim808.setPhoneFunctionality(fun); 396 | Log.notice(S_F("phone functionality : %S" NL), success ? TO_F(strings::SUCCESS) : TO_F(strings::FAILED)); 397 | } 398 | } 399 | } 400 | 401 | void gps() { 402 | char position[128]; 403 | 404 | Log.notice(S_F("GPS" NL)); 405 | readNext(); 406 | 407 | if(BUFFER_IS("RAW") || BUFFER_IS("PARSE")) { 408 | sim808.getGpsPosition(position, 128); 409 | Log.notice(S_F("position\t\t: \"%s\"" NL), position); 410 | } 411 | 412 | if(BUFFER_IS("PARSE")) { 413 | char * timeStr; 414 | float tmpFloat; 415 | uint16_t tmpInt; 416 | bool oneField; 417 | 418 | SIM808GpsStatus status = sim808.getGpsStatus(position, 128); 419 | __StrPtr state; 420 | 421 | switch(status) { 422 | case SIM808GpsStatus::Fail: 423 | state = TO_F(strings::FAILED); 424 | break; 425 | case SIM808GpsStatus::Off: 426 | state = TO_F(strings::OFF); 427 | break; 428 | case SIM808GpsStatus::NoFix: 429 | state = S_F("NoFix"); 430 | break; 431 | case SIM808GpsStatus::Fix: 432 | state = S_F("Fix"); 433 | break; 434 | case SIM808GpsStatus::AccurateFix: 435 | state = S_F("AccurateFix"); 436 | break; 437 | default: 438 | state = TO_F(strings::UNKNOWN); 439 | break; 440 | } 441 | 442 | Log.notice(S_F("status\t\t: %S" NL), state); 443 | 444 | readNext(); 445 | if(BUFFER_IS_P(strings::ALL) || BUFFER_IS("TIME")) { 446 | oneField = true; 447 | sim808.getGpsField(position, SIM808GpsField::Utc, &timeStr); 448 | Log.notice(S_F("time\t\t\t: %s" NL), timeStr); 449 | } 450 | 451 | if(BUFFER_IS_P(strings::ALL) || BUFFER_IS("LAT")) { 452 | oneField = true; 453 | sim808.getGpsField(position, SIM808GpsField::Latitude, &tmpFloat); 454 | Log.notice(S_F("latitude\t\t: %F" NL), tmpFloat); 455 | } 456 | 457 | if(BUFFER_IS_P(strings::ALL) || BUFFER_IS("LON")) { 458 | oneField = true; 459 | sim808.getGpsField(position, SIM808GpsField::Longitude, &tmpFloat); 460 | Log.notice(S_F("longitude\t\t: %F" NL), tmpFloat); 461 | } 462 | 463 | if(BUFFER_IS_P(strings::ALL) || BUFFER_IS("ALT")) { 464 | oneField = true; 465 | sim808.getGpsField(position, SIM808GpsField::Altitude, &tmpFloat); 466 | Log.notice(S_F("altitude\t\t: %F" NL), tmpFloat); 467 | } 468 | 469 | if(BUFFER_IS_P(strings::ALL) || BUFFER_IS("SPEED")) { 470 | oneField = true; 471 | sim808.getGpsField(position, SIM808GpsField::Speed, &tmpFloat); 472 | Log.notice(S_F("speed\t\t: %F" NL), tmpFloat); 473 | } 474 | 475 | if(BUFFER_IS_P(strings::ALL) || BUFFER_IS("COURSE")) { 476 | oneField = true; 477 | sim808.getGpsField(position, SIM808GpsField::Course, &tmpFloat); 478 | Log.notice(S_F("course\t\t: %F" NL), tmpFloat); 479 | } 480 | 481 | if(BUFFER_IS_P(strings::ALL) || BUFFER_IS("SATVIEW")) { 482 | oneField = true; 483 | sim808.getGpsField(position, SIM808GpsField::GpsInView, &tmpInt); 484 | Log.notice(S_F("satellites in view\t: %d" NL), tmpInt); 485 | } 486 | 487 | if(BUFFER_IS_P(strings::ALL) || BUFFER_IS("SATUSED")) { 488 | oneField = true; 489 | sim808.getGpsField(position, SIM808GpsField::GnssUsed, &tmpInt); 490 | Log.notice(S_F("satellites used\t: %d" NL), tmpInt); 491 | } 492 | 493 | if(!oneField) { 494 | unrecognized(); 495 | return; 496 | } 497 | } 498 | } 499 | 500 | void sendHttp() { 501 | bool defaultUrl = readNext(); 502 | char action[5]; 503 | char url[50]; 504 | 505 | strlcpy(action, buffer, 5); 506 | 507 | if(defaultUrl) STRLCPY_P(url, strings::DEFAULT_URL, 50); 508 | else { 509 | readNext(); 510 | strlcpy(url, buffer, 50); 511 | } 512 | 513 | if(STRING_IS(action, "GET")) { 514 | uint16_t code = sim808.httpGet(url, buffer, BUFFER_SIZE); 515 | 516 | Log.notice(S_F("HTTP" NL)); 517 | Log.notice(S_F("Server responded : %d" NL), code); 518 | Log.notice(S_F("\"%s\""), buffer); 519 | } 520 | else if(STRING_IS(action, "POST")) { 521 | PRINT("[body] ?"); 522 | readNext(true); 523 | uint16_t code = sim808.httpPost(url, S_F("text/plain"), buffer, buffer, BUFFER_SIZE); 524 | 525 | Log.notice(S_F("HTTP" NL)); 526 | Log.notice(S_F("Server responded : %d" NL), code); 527 | Log.notice(S_F("\"%s\""), buffer); 528 | } 529 | else { 530 | unrecognized(); 531 | return; 532 | } 533 | } 534 | 535 | void send() { 536 | readNext(); 537 | 538 | if(BUFFER_IS("SMS")) { 539 | char number[20]; 540 | 541 | readNext(); 542 | strncpy(number, buffer, 20); 543 | 544 | PRINT("[message] ?"); 545 | readNext(true); //read the whole line into buffer 546 | bool success = sim808.sendSms(number, buffer); 547 | Log.notice(S_F("send SMS : %S" NL), success ? TO_F(strings::SUCCESS) : TO_F(strings::FAILED)); 548 | } 549 | else if(BUFFER_IS("HTTP")) return sendHttp(); 550 | else if(BUFFER_IS("AT")) { 551 | readNext(true); 552 | sim808.sendCommand(buffer, buffer, BUFFER_SIZE); 553 | 554 | Log.notice(S_F("response : %s" NL), buffer); 555 | } 556 | else { 557 | unrecognized(); 558 | return; 559 | } 560 | } 561 | 562 | void setup() { 563 | while(!Serial); 564 | 565 | Serial.begin(SERIAL_BAUDRATE); 566 | Log.begin(LOG_LEVEL_NOTICE, &Serial); 567 | 568 | simSerial.begin(SIM808_BAUDRATE); 569 | sim808.begin(simSerial); 570 | 571 | Log.notice(S_F("Powering on SIM808..." NL)); 572 | sim808.powerOnOff(true); 573 | sim808.init(); 574 | 575 | usage(); 576 | } 577 | 578 | void loop() { 579 | readNext(); 580 | 581 | if(BUFFER_IS("?")) usage(); 582 | else if(BUFFER_IS("reset")) reset(); 583 | else if(BUFFER_IS("init")) init_(); 584 | else if(BUFFER_IS("power")) power(); 585 | else if(BUFFER_IS("charge")) charge(); 586 | else if(BUFFER_IS("sim")) sim(); 587 | else if(BUFFER_IS("imei")) imei(); 588 | else if(BUFFER_IS("send")) send(); 589 | else if(BUFFER_IS_P(strings::NETWORK)) network(); 590 | else if(BUFFER_IS_P(strings::GPS)) gps(); 591 | else { 592 | unrecognized(); 593 | return; 594 | } 595 | 596 | Log.notice(S_F("Done" NL)); 597 | } -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=SIM808 2 | version=2.0.0 3 | author=Bertrand Lemasle 4 | maintainer=Bertrand Lemasle 5 | sentence=Straightforward Arduino library for the SIM808 6 | paragraph=Provides easy access to several SIM808 features, including HTTP(s) GET & POST, sending SMS and GPS positioning. 7 | category=Device Control 8 | url=https://github.com/blemasle/arduino-sim808 9 | architectures=* 10 | includes=SIM808.h -------------------------------------------------------------------------------- /src/SIM808.Gprs.cpp: -------------------------------------------------------------------------------- 1 | #include "SIM808.h" 2 | 3 | AT_COMMAND(SET_BEARER_SETTING_PARAMETER, "+SAPBR=3,1,\"%S\",\"%s\""); 4 | AT_COMMAND(SET_BEARER_SETTING, "+SAPBR=%d,%d"); 5 | AT_COMMAND(GPRS_ATTACH, "+CGATT=%d"); 6 | 7 | AT_COMMAND_PARAMETER(BEARER, CONTYPE); 8 | AT_COMMAND_PARAMETER(BEARER, APN); 9 | AT_COMMAND_PARAMETER(BEARER, USER); 10 | AT_COMMAND_PARAMETER(BEARER, PWD); 11 | 12 | TOKEN_TEXT(GPRS, "GPRS"); 13 | TOKEN_TEXT(CGATT, "+CGATT"); 14 | TOKEN_TEXT(CIPSHUT, "+CIPSHUT"); 15 | TOKEN_TEXT(SHUT_OK, "SHUT OK"); 16 | TOKEN_TEXT(CGREG, "+CGREG"); 17 | 18 | bool SIM808::setBearerSetting(ATConstStr parameter, const char* value) 19 | { 20 | sendFormatAT(TO_F(AT_COMMAND_SET_BEARER_SETTING_PARAMETER), parameter, value); 21 | return waitResponse() == 0; 22 | } 23 | 24 | bool SIM808::getGprsPowerState(bool *state) 25 | { 26 | uint8_t result; 27 | 28 | sendAT(TO_F(TOKEN_CGATT), TO_F(TOKEN_READ)); 29 | 30 | if(waitResponse(10000L, TO_F(TOKEN_CGATT)) != 0 || 31 | !parseReply(',', 0, &result) || 32 | waitResponse()) 33 | return false; 34 | 35 | *state = result; 36 | return true; 37 | } 38 | 39 | bool SIM808::enableGprs(const char *apn, const char* user, const char *password) 40 | { 41 | char gprsToken[5]; 42 | strcpy_P(gprsToken, TOKEN_GPRS); 43 | 44 | return 45 | (sendAT(TO_F(TOKEN_CIPSHUT)), waitResponse(65000L, TO_F(TOKEN_SHUT_OK)) == 0) && //AT+CIPSHUT 46 | (sendFormatAT(TO_F(AT_COMMAND_GPRS_ATTACH), 1), waitResponse(10000L) == 0) && //AT+CGATT=1 47 | 48 | (setBearerSetting(TO_F(AT_COMMAND_PARAMETER_BEARER_CONTYPE), gprsToken)) && //AT+SAPBR=3,1,"CONTYPE","GPRS" 49 | 50 | (setBearerSetting(TO_F(AT_COMMAND_PARAMETER_BEARER_APN), apn)) && //AT+SAPBR=3,1,"APN","xxx" 51 | (user == NULL || setBearerSetting(TO_F(AT_COMMAND_PARAMETER_BEARER_USER), user)) && //AT+SAPBR=3,1,"USER","xxx" 52 | (password == NULL || setBearerSetting(TO_F(AT_COMMAND_PARAMETER_BEARER_PWD), password)) && //AT+SAPBR=3,1,"PWD","xxx" 53 | 54 | (sendFormatAT(TO_F(AT_COMMAND_SET_BEARER_SETTING), 1, 1), waitResponse(65000L) == 0); //AT+SAPBR=1,1 55 | } 56 | 57 | bool SIM808::disableGprs() 58 | { 59 | return 60 | (sendFormatAT(TO_F(AT_COMMAND_SET_BEARER_SETTING), 0, 1), waitResponse(65000L) != -1) && //AT+SAPBR=0,1 61 | (sendAT(TO_F(TOKEN_CIPSHUT)), waitResponse(65000L, TO_F(TOKEN_SHUT_OK)) == 0) && //AT+CIPSHUT 62 | (sendFormatAT(TO_F(AT_COMMAND_GPRS_ATTACH), 0), waitResponse(10000L) == 0); //AT+CGATT=0 63 | } 64 | 65 | SIM808NetworkRegistrationState SIM808::getNetworkRegistrationStatus() 66 | { 67 | uint8_t stat; 68 | sendAT(TO_F(TOKEN_CGREG), TO_F(TOKEN_READ)); 69 | 70 | if(waitResponse(TO_F(TOKEN_CGREG)) != 0 || 71 | !parseReply(',', (uint8_t)SIM808RegistrationStatusResponse::Stat, &stat) || 72 | waitResponse() != 0) 73 | return SIM808NetworkRegistrationState::Error; 74 | 75 | return (SIM808NetworkRegistrationState)stat; 76 | } 77 | -------------------------------------------------------------------------------- /src/SIM808.Gps.cpp: -------------------------------------------------------------------------------- 1 | #include "SIM808.h" 2 | 3 | TOKEN_TEXT(GPS_POWER, "+CGNSPWR"); 4 | TOKEN_TEXT(GPS_INFO, "+CGNSINF"); 5 | 6 | bool SIM808::powerOnOffGps(bool power) 7 | { 8 | bool currentState; 9 | if(!getGpsPowerState(¤tState) || (currentState == power)) return false; 10 | 11 | sendAT(TO_F(TOKEN_GPS_POWER), TO_F(TOKEN_WRITE), (uint8_t)power); 12 | return waitResponse() == 0; 13 | } 14 | 15 | bool SIM808::getGpsPosition(char *response, size_t responseSize) 16 | { 17 | sendAT(TO_F(TOKEN_GPS_INFO)); 18 | 19 | if(waitResponse(TO_F(TOKEN_GPS_INFO)) != 0) 20 | return false; 21 | 22 | // GPSINF response might be too long for the reply buffer 23 | copyCurrentLine(response, responseSize, strlen_P(TOKEN_GPS_INFO) + 2); 24 | } 25 | 26 | void SIM808::getGpsField(const char* response, SIM808GpsField field, char** result) 27 | { 28 | char *pTmp = find(response, ',', (uint8_t)field); 29 | *result = pTmp; 30 | } 31 | 32 | bool SIM808::getGpsField(const char* response, SIM808GpsField field, uint16_t* result) 33 | { 34 | if (field < SIM808GpsField::Speed) return false; 35 | 36 | parse(response, ',', (uint8_t)field, result); 37 | return true; 38 | } 39 | 40 | bool SIM808::getGpsField(const char* response, SIM808GpsField field, float* result) 41 | { 42 | if (field != SIM808GpsField::Course && 43 | field != SIM808GpsField::Latitude && 44 | field != SIM808GpsField::Longitude && 45 | field != SIM808GpsField::Altitude && 46 | field != SIM808GpsField::Speed) return false; 47 | 48 | parse(response, ',', (uint8_t)field, result); 49 | return true; 50 | } 51 | 52 | SIM808GpsStatus SIM808::getGpsStatus(char * response, size_t responseSize, uint8_t minSatellitesForAccurateFix) 53 | { 54 | SIM808GpsStatus result = SIM808GpsStatus::NoFix; 55 | 56 | sendAT(TO_F(TOKEN_GPS_INFO)); 57 | 58 | if(waitResponse(TO_F(TOKEN_GPS_INFO)) != 0) 59 | return SIM808GpsStatus::Fail; 60 | 61 | uint16_t shift = strlen_P(TOKEN_GPS_INFO) + 2; 62 | 63 | if(replyBuffer[shift] == '0') result = SIM808GpsStatus::Off; 64 | if(replyBuffer[shift + 2] == '1') // fix acquired 65 | { 66 | uint16_t satellitesUsed; 67 | getGpsField(replyBuffer, SIM808GpsField::GnssUsed, &satellitesUsed); 68 | 69 | result = satellitesUsed > minSatellitesForAccurateFix ? 70 | SIM808GpsStatus::AccurateFix : 71 | SIM808GpsStatus::Fix; 72 | 73 | copyCurrentLine(response, responseSize, shift); 74 | } 75 | 76 | if(waitResponse() != 0) return SIM808GpsStatus::Fail; 77 | 78 | return result; 79 | } 80 | 81 | bool SIM808::getGpsPowerState(bool *state) 82 | { 83 | uint8_t result; 84 | 85 | sendAT(TO_F(TOKEN_GPS_POWER), TO_F(TOKEN_READ)); 86 | 87 | if(waitResponse(10000L, TO_F(TOKEN_GPS_POWER)) != 0 || 88 | !parseReply(',', 0, &result) || 89 | waitResponse()) 90 | return false; 91 | 92 | *state = result; 93 | return true; 94 | } 95 | -------------------------------------------------------------------------------- /src/SIM808.Gsm.cpp: -------------------------------------------------------------------------------- 1 | #include "SIM808.h" 2 | 3 | AT_COMMAND(SEND_SMS, "+CMGS=\"%s\""); 4 | 5 | TOKEN_TEXT(CPIN, "+CPIN"); 6 | TOKEN_TEXT(CSQ, "+CSQ"); 7 | TOKEN_TEXT(CMGS, "+CMGS"); 8 | 9 | bool SIM808::simUnlock(const char* pin) 10 | { 11 | sendAT(TO_F(TOKEN_CPIN), TO_F(TOKEN_WRITE), pin); 12 | 13 | return waitResponse(5000L) == 0; 14 | } 15 | 16 | size_t SIM808::getSimState(char *state, size_t stateSize) 17 | { 18 | sendAT(TO_F(TOKEN_CPIN), TO_F(TOKEN_READ)); 19 | if(waitResponse(5000L, TO_F(TOKEN_CPIN)) != 0) return 0; 20 | 21 | copyCurrentLine(state, stateSize, strlen_P(TOKEN_CPIN) + 2); 22 | 23 | return waitResponse() == 0 ? 24 | strlen(state) : 25 | 0; 26 | } 27 | 28 | size_t SIM808::getImei(char *imei, size_t imeiSize) 29 | { 30 | //AT+GSN does not have a response prefix, so we need to flush input 31 | //before sending the command 32 | flushInput(); 33 | 34 | sendAT(S_F("+GSN")); 35 | waitResponse(SIMCOMAT_DEFAULT_TIMEOUT, NULL); //consuming an extra line before the response. Undocumented 36 | 37 | if(waitResponse(SIMCOMAT_DEFAULT_TIMEOUT, NULL) != 0) return 0; 38 | copyCurrentLine(imei, imeiSize); 39 | 40 | return waitResponse() == 0? 41 | strlen(imei) : 42 | 0; 43 | } 44 | 45 | SIM808SignalQualityReport SIM808::getSignalQuality() 46 | { 47 | uint8_t quality; 48 | uint8_t errorRate; 49 | 50 | SIM808SignalQualityReport report = {99, 99, 1}; 51 | 52 | sendAT(TO_F(TOKEN_CSQ)); 53 | if(waitResponse(TO_F(TOKEN_CSQ)) != 0 || 54 | !parseReply(',', (uint8_t)SIM808SignalQualityResponse::SignalStrength, &quality) || 55 | !parseReply(',', (uint8_t)SIM808SignalQualityResponse::BitErrorrate, &errorRate) || 56 | waitResponse()) 57 | return report; 58 | 59 | report.rssi = quality; 60 | report.ber = errorRate; 61 | 62 | if (quality == 0) report.attenuation = -115; 63 | else if (quality == 1) report.attenuation = -111; 64 | else if (quality == 31) report.attenuation = -52; 65 | else if (quality > 31) report.attenuation = 1; 66 | else report.attenuation = map(quality, 2, 30, -110, -54); 67 | 68 | return report; 69 | } 70 | 71 | bool SIM808::setSmsMessageFormat(SIM808SmsMessageFormat format) 72 | { 73 | sendAT(S_F("+CMGF="), (uint8_t)format); 74 | return waitResponse() == 0; 75 | } 76 | 77 | bool SIM808::sendSms(const char *addr, const char *msg) 78 | { 79 | if (!setSmsMessageFormat(SIM808SmsMessageFormat::Text)) return false; 80 | sendFormatAT(TO_F(AT_COMMAND_SEND_SMS), addr); 81 | 82 | if (!waitResponse(S_F(">")) == 0) return false; 83 | 84 | SENDARROW; 85 | print(msg); 86 | print((char)0x1A); 87 | 88 | return waitResponse(60000L, TO_F(TOKEN_CMGS)) == 0 && 89 | waitResponse() == 0; 90 | } -------------------------------------------------------------------------------- /src/SIM808.Http.cpp: -------------------------------------------------------------------------------- 1 | #include "SIM808.h" 2 | 3 | AT_COMMAND(SET_HTTP_PARAMETER_STRING, "+HTTPPARA=\"%S\",\"%s\""); 4 | AT_COMMAND(SET_HTTP_PARAMETER_STRING_PROGMEM, "+HTTPPARA=\"%S\",\"%S\""); 5 | AT_COMMAND(SET_HTTP_PARAMETER_INT, "+HTTPPARA=\"%S\",\"%d\""); 6 | AT_COMMAND(HTTP_DATA, "+HTTPDATA=%d,%d"); 7 | AT_COMMAND(HTTP_READ, "+HTTPREAD=%d,%d"); 8 | 9 | TOKEN_TEXT(HTTP_DATA, "+HTTPDATA"); 10 | TOKEN_TEXT(HTTP_ACTION, "+HTTPACTION"); 11 | TOKEN_TEXT(HTTP_READ, "+HTTPREAD"); 12 | TOKEN_TEXT(HTTP_INIT, "+HTTPINIT"); 13 | TOKEN_TEXT(HTTP_SSL, "+HTTPSSL"); 14 | TOKEN_TEXT(HTTP_TERM, "+HTTPTERM"); 15 | TOKEN(DOWNLOAD); 16 | 17 | AT_COMMAND_PARAMETER(HTTP, CONTENT); 18 | AT_COMMAND_PARAMETER(HTTP, REDIR); 19 | AT_COMMAND_PARAMETER(HTTP, CID); 20 | AT_COMMAND_PARAMETER(HTTP, URL); 21 | AT_COMMAND_PARAMETER(HTTP, UA); 22 | 23 | 24 | bool SIM808::setHttpParameter(ATConstStr parameter, ATConstStr value) 25 | { 26 | sendFormatAT(TO_F(AT_COMMAND_SET_HTTP_PARAMETER_STRING_PROGMEM), parameter, value); 27 | return waitResponse() == 0; 28 | } 29 | 30 | #if defined(__AVR__) 31 | 32 | bool SIM808::setHttpParameter(ATConstStr parameter, const char * value) 33 | { 34 | sendFormatAT(TO_F(AT_COMMAND_SET_HTTP_PARAMETER_STRING), parameter, value); 35 | return waitResponse() == 0; 36 | } 37 | 38 | #endif 39 | 40 | bool SIM808::setHttpParameter(ATConstStr parameter, uint8_t value) 41 | { 42 | sendFormatAT(TO_F(AT_COMMAND_SET_HTTP_PARAMETER_INT), parameter, value); 43 | return waitResponse() == 0; 44 | } 45 | 46 | uint16_t SIM808::httpGet(const char *url, char *response, size_t responseSize) 47 | { 48 | uint16_t statusCode = 0; 49 | size_t dataSize = 0; 50 | 51 | bool result = setupHttpRequest(url) && 52 | fireHttpRequest(SIM808HttpAction::Get, &statusCode, &dataSize) && 53 | readHttpResponse(response, responseSize, dataSize) && 54 | httpEnd(); 55 | 56 | return statusCode; 57 | } 58 | 59 | uint16_t SIM808::httpPost(const char *url, ATConstStr contentType, const char *body, char *response, size_t responseSize) 60 | { 61 | uint16_t statusCode = 0; 62 | size_t dataSize = 0; 63 | 64 | bool result = setupHttpRequest(url) && 65 | setHttpParameter(TO_F(AT_COMMAND_PARAMETER_HTTP_CONTENT), contentType) && 66 | setHttpBody(body) && 67 | fireHttpRequest(SIM808HttpAction::Post, &statusCode, &dataSize) && 68 | readHttpResponse(response, responseSize, dataSize) && 69 | httpEnd(); 70 | 71 | return statusCode; 72 | } 73 | 74 | bool SIM808::setupHttpRequest(const char* url) 75 | { 76 | httpEnd(); 77 | 78 | return httpInit() && 79 | setHttpParameter(TO_F(AT_COMMAND_PARAMETER_HTTP_REDIR), 1) && 80 | setHttpParameter(TO_F(AT_COMMAND_PARAMETER_HTTP_CID), 1) && 81 | setHttpParameter(TO_F(AT_COMMAND_PARAMETER_HTTP_URL), url) && 82 | (url[4] != 's' || (sendAT(TO_F(TOKEN_HTTP_SSL), TO_F(TOKEN_WRITE), 1), waitResponse() == 0)) && 83 | (_userAgent == NULL || setHttpParameter(TO_F(AT_COMMAND_PARAMETER_HTTP_UA), _userAgent)); 84 | } 85 | 86 | bool SIM808::httpInit() 87 | { 88 | return (sendAT(TO_F(TOKEN_HTTP_INIT)), waitResponse() == 0); 89 | } 90 | 91 | bool SIM808::httpEnd() 92 | { 93 | return (sendAT(TO_F(TOKEN_HTTP_TERM)), waitResponse() == 0); 94 | } 95 | 96 | bool SIM808::setHttpBody(const char* body) 97 | { 98 | sendFormatAT(TO_F(AT_COMMAND_HTTP_DATA), strlen(body), 10000L); 99 | 100 | if(waitResponse(TO_F(TOKEN_DOWNLOAD)) != 0) return false; 101 | 102 | SENDARROW; 103 | print(body); 104 | 105 | if(waitResponse() != 0) return false; 106 | return true; 107 | } 108 | 109 | bool SIM808::fireHttpRequest(const SIM808HttpAction action, uint16_t *statusCode, size_t *dataSize) 110 | { 111 | sendAT(TO_F(TOKEN_HTTP_ACTION), TO_F(TOKEN_WRITE), (uint8_t)action); 112 | 113 | return waitResponse(HTTP_TIMEOUT, TO_F(TOKEN_HTTP_ACTION)) == 0 && 114 | parseReply(',', (uint8_t)SIM808HttpActionResponse::StatusCode, statusCode) && 115 | parseReply(',', (uint8_t)SIM808HttpActionResponse::DataLen, dataSize); 116 | } 117 | 118 | bool SIM808::readHttpResponse(char *response, size_t responseSize, size_t dataSize) 119 | { 120 | size_t readSize = min(responseSize - 1, dataSize); 121 | 122 | sendFormatAT(TO_F(AT_COMMAND_HTTP_READ), 0, readSize); 123 | if(waitResponse(TO_F(TOKEN_HTTP_READ)) != 0) return false; 124 | 125 | readNext(response, readSize + 1); // taking in account the string term 126 | return waitResponse() == 0; 127 | } 128 | -------------------------------------------------------------------------------- /src/SIM808.Power.cpp: -------------------------------------------------------------------------------- 1 | #include "SIM808.h" 2 | 3 | TOKEN_TEXT(CBC, "+CBC"); 4 | TOKEN_TEXT(CFUN, "+CFUN"); 5 | 6 | bool SIM808::powered() 7 | { 8 | if(_statusPin == SIM808_UNAVAILABLE_PIN) { 9 | sendAT(); 10 | return waitResponse(SIMCOMAT_DEFAULT_TIMEOUT) != -1; 11 | } 12 | 13 | return digitalRead(_statusPin) == HIGH; 14 | } 15 | 16 | bool SIM808::powerOnOff(bool power) 17 | { 18 | if (_pwrKeyPin == SIM808_UNAVAILABLE_PIN) return false; 19 | 20 | bool currentlyPowered = powered(); 21 | if (currentlyPowered == power) return false; 22 | 23 | SIM808_PRINT_P("powerOnOff: %t", power); 24 | 25 | digitalWrite(_pwrKeyPin, LOW); 26 | delay(2000); 27 | digitalWrite(_pwrKeyPin, HIGH); 28 | 29 | int16_t timeout = 2000; 30 | do { 31 | delay(150); 32 | timeout -= 150; 33 | currentlyPowered = powered(); 34 | } while(currentlyPowered != power && timeout > 0); 35 | 36 | return currentlyPowered == power; 37 | } 38 | 39 | SIM808ChargingStatus SIM808::getChargingState() 40 | { 41 | uint8_t state; 42 | uint8_t level; 43 | uint16_t voltage; 44 | 45 | sendAT(TO_F(TOKEN_CBC)); 46 | 47 | if (waitResponse(TO_F(TOKEN_CBC)) == 0 && 48 | parseReply(',', (uint8_t)SIM808BatteryChargeField::Bcs, &state) && 49 | parseReply(',', (uint8_t)SIM808BatteryChargeField::Bcl, &level) && 50 | parseReply(',', (uint16_t)SIM808BatteryChargeField::Voltage, &voltage) && 51 | waitResponse() == 0) 52 | return { (SIM808ChargingState)state, level, voltage }; 53 | 54 | return { SIM808ChargingState::Error, 0, 0 }; 55 | } 56 | 57 | SIM808PhoneFunctionality SIM808::getPhoneFunctionality() 58 | { 59 | uint8_t state; 60 | 61 | sendAT(TO_F(TOKEN_CFUN), TO_F(TOKEN_READ)); 62 | 63 | if (waitResponse(10000L, TO_F(TOKEN_CFUN)) == 0 && 64 | parseReply(',', 0, &state) && 65 | waitResponse() == 0) 66 | return (SIM808PhoneFunctionality)state; 67 | 68 | return SIM808PhoneFunctionality::Fail; 69 | } 70 | 71 | bool SIM808::setPhoneFunctionality(SIM808PhoneFunctionality fun) 72 | { 73 | sendAT(TO_F(TOKEN_CFUN), TO_F(TOKEN_WRITE), (uint8_t)fun); 74 | 75 | return waitResponse(10000L) == 0; 76 | } 77 | 78 | bool SIM808::setSlowClock(SIM808SlowClock mode) 79 | { 80 | sendAT(S_F("+CSCLK"), TO_F(TOKEN_WRITE), (uint8_t)mode); 81 | 82 | return waitResponse() == 0; 83 | } 84 | 85 | -------------------------------------------------------------------------------- /src/SIM808.Types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | enum class SIM808Echo : uint8_t 6 | { 7 | Off = 0, 8 | On = 1 9 | }; 10 | 11 | enum class SIM808SmsMessageFormat : uint8_t 12 | { 13 | Pdu = 0, 14 | Text = 1 15 | }; 16 | 17 | /** 18 | * List of supported HTTP methods. 19 | */ 20 | enum class SIM808HttpAction : uint8_t 21 | { 22 | Get = 0, 23 | Post = 1, 24 | Head = 2 25 | }; 26 | 27 | /** 28 | * Fields returned by the AT+HTTPACTION command. 29 | */ 30 | enum class SIM808HttpActionResponse : uint8_t 31 | { 32 | Method = 0, ///< HTTP method. 33 | StatusCode = 1, ///< Status code responded by the remote server. 34 | DataLen = 2 ///< Response body length. 35 | }; 36 | 37 | /** 38 | * Fields returned by the AT+CGREG command. 39 | */ 40 | enum class SIM808RegistrationStatusResponse : uint8_t 41 | { 42 | N = 0, ///< Controls network registration unsolicited result code. 43 | Stat = 1, ///< Current registration status. See SIM808NetworkRegistrationState. 44 | Lac = 2, ///< Location information. 45 | Ci = 3 ///< Location information. 46 | }; 47 | 48 | /** 49 | * Fields return by the AT+CSQ command. 50 | */ 51 | enum class SIM808SignalQualityResponse : uint8_t 52 | { 53 | SignalStrength = 0, ///< Received Signal Strength Indication 54 | BitErrorrate = 1 ///< Bit Error Rate 55 | }; 56 | 57 | enum class SIM808PhoneFunctionality : int8_t 58 | { 59 | Fail = -1, ///< Reading the current phone functionality has failed. 60 | Minimum = 0, ///< Minimum functionality. 61 | Full = 1, ///< Full functionality (default on device power on). 62 | Disabled = 4 ///< Disable phone both transmit and receive RF circuit. 63 | }; 64 | 65 | enum class SIM808GpsStatus : int8_t 66 | { 67 | Fail = -1, ///< Reading the current GPS position has failed. 68 | Off = 0, ///< GPS is off. 69 | NoFix = 1, ///< A fix is not acquired yet. 70 | Fix = 2, ///< A fix is acquired. 71 | AccurateFix = 3 ///< An accurate fix is acquired, using more than GPS_ACCURATE_FIX_MIN_SATELLITES. 72 | }; 73 | 74 | enum class SIM808GpsField : uint8_t 75 | { 76 | Utc = 2, ///< UTC date time, as yyyyMMddhhmmss.000. 77 | Latitude = 3, ///< Latitude in degrees. 78 | Longitude = 4, ///< Longitude in degress. 79 | Altitude = 5, ///< Altitude in meters. 80 | Speed = 6, ///< Speed over ground in km/h. 81 | Course = 7, ///< Course over ground in degrees. 82 | GpsInView = 14, ///< GPS satellites in view. 83 | GnssUsed = 15 ///< GPS satellites used to acquire the position. 84 | }; 85 | 86 | enum class SIM808BatteryChargeField : uint8_t 87 | { 88 | Bcs = 0, ///< Battery Charge State. 89 | Bcl = 1, ///< Battery Charge Level. 90 | Voltage = 2 ///< Battery voltage. 91 | }; 92 | 93 | enum class SIM808SlowClock : uint8_t 94 | { 95 | Disable = 0, ///< Disables slow clock, module will not enter sleep mode 96 | Enable = 1, ///< Enables slow clock, controlled by DTR pin. 97 | Auto = 2 ///< Enables slow clock automatically. 98 | }; 99 | 100 | enum class SIM808ChargingState : int8_t 101 | { 102 | Error = -1, ///< Reading the current charging status has failed. 103 | NotCharging = 0, ///< Not charging. 104 | Charging = 1, ///< Charging. 105 | ChargingDone = 2 ///< Plugged in, but charging done. 106 | }; 107 | 108 | enum class SIM808NetworkRegistrationState : int8_t 109 | { 110 | Error = -1, ///< Reading the current network registration status as failed. 111 | NotSearching = 0, ///< Not searching. 112 | Registered = 1, ///< Registered to the home network. 113 | Searching = 2, ///< Not registered but searching for a network to register. 114 | Denied = 3, ///< Registration has been denied by the provider. Stopped searching. 115 | Unknown = 4, ///< Unknown 116 | Roaming = 5 ///< Registered to a network that is not the home network. 117 | }; 118 | 119 | struct SIM808SignalQualityReport 120 | { 121 | uint8_t rssi; ///< Received Signal Strength Indication, from 0 (worst) to 31 (best). 99 means unknown. 122 | uint8_t ber; ///< Bit Error Rate, from 0 to 7. 99 means unknown. 123 | int8_t attenuation; ///< Estimad signal attenuation from rssi, expressed in dBm. 124 | }; 125 | 126 | struct SIM808ChargingStatus 127 | { 128 | SIM808ChargingState state; ///< Current charging state. 129 | int8_t level; ///< Battery level, expressed as a percentage. 130 | int16_t voltage; ///< Battery level, expressed in mV. 131 | }; -------------------------------------------------------------------------------- /src/SIM808.cpp: -------------------------------------------------------------------------------- 1 | #include "SIM808.h" 2 | 3 | TOKEN(RDY); 4 | 5 | SIM808::SIM808(uint8_t resetPin, uint8_t pwrKeyPin, uint8_t statusPin) 6 | { 7 | _resetPin = resetPin; 8 | _pwrKeyPin = pwrKeyPin; 9 | _statusPin = statusPin; 10 | 11 | pinMode(_resetPin, OUTPUT); 12 | if(_pwrKeyPin != SIM808_UNAVAILABLE_PIN) pinMode(_pwrKeyPin, OUTPUT); 13 | if (_statusPin != SIM808_UNAVAILABLE_PIN) pinMode(_statusPin, INPUT); 14 | 15 | if(_pwrKeyPin != SIM808_UNAVAILABLE_PIN) digitalWrite(_pwrKeyPin, HIGH); 16 | digitalWrite(_resetPin, HIGH); 17 | } 18 | 19 | SIM808::~SIM808() { } 20 | 21 | #pragma region Public functions 22 | 23 | void SIM808::init() 24 | { 25 | SIM808_PRINT_SIMPLE_P("Init..."); 26 | 27 | reset(); 28 | waitForReady(); 29 | delay(1500); 30 | 31 | setEcho(SIM808Echo::Off); 32 | } 33 | 34 | void SIM808::reset() 35 | { 36 | digitalWrite(_resetPin, HIGH); 37 | delay(10); 38 | digitalWrite(_resetPin, LOW); 39 | delay(200); 40 | 41 | digitalWrite(_resetPin, HIGH); 42 | } 43 | 44 | void SIM808::waitForReady() 45 | { 46 | do 47 | { 48 | SIM808_PRINT_SIMPLE_P("Waiting for echo..."); 49 | sendAT(S_F("")); 50 | // Despite official documentation, we can get an "AT" back without a "RDY" first. 51 | } while (waitResponse(TO_F(TOKEN_AT)) != 0); 52 | 53 | // we got AT, waiting for RDY 54 | while (waitResponse(TO_F(TOKEN_RDY)) != 0); 55 | } 56 | 57 | bool SIM808::setEcho(SIM808Echo mode) 58 | { 59 | sendAT(S_F("E"), (uint8_t)mode); 60 | 61 | return waitResponse() == 0; 62 | } 63 | 64 | size_t SIM808::sendCommand(const char *cmd, char *response, size_t responseSize) 65 | { 66 | flushInput(); 67 | sendAT(cmd); 68 | 69 | uint16_t timeout = SIMCOMAT_DEFAULT_TIMEOUT; 70 | readNext(response, responseSize, &timeout); 71 | } 72 | 73 | #pragma endregion 74 | 75 | 76 | -------------------------------------------------------------------------------- /src/SIM808.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "SIM808.Types.h" 5 | 6 | #define HTTP_TIMEOUT 10000L 7 | #define GPS_ACCURATE_FIX_MIN_SATELLITES 4 8 | #define SIM808_UNAVAILABLE_PIN 255 9 | 10 | class SIM808 : public SIMComAT 11 | { 12 | private: 13 | uint8_t _resetPin; 14 | uint8_t _statusPin; 15 | uint8_t _pwrKeyPin; 16 | const char* _userAgent; 17 | 18 | /** 19 | * Wait for the device to be ready to accept communcation. 20 | */ 21 | void waitForReady(); 22 | 23 | /** 24 | * Set all the parameters up for a HTTP request to be fired next. 25 | */ 26 | bool setupHttpRequest(const char* url); 27 | /** 28 | * Fire a HTTP request and return the server response code and body size. 29 | */ 30 | bool fireHttpRequest(const SIM808HttpAction action, uint16_t *statusCode, size_t *dataSize); 31 | /** 32 | * Read the last HTTP response body into response. 33 | */ 34 | bool readHttpResponse(char *response, size_t responseSize, size_t dataSize); 35 | bool setHttpParameter(ATConstStr parameter, ATConstStr value); 36 | #if defined(__AVR__) 37 | bool setHttpParameter(ATConstStr parameter, const char * value); 38 | #endif 39 | bool setHttpParameter(ATConstStr parameter, uint8_t value); 40 | /** 41 | * Set the HTTP body of the next request to be fired. 42 | */ 43 | bool setHttpBody(const char* body); 44 | /** 45 | * Initialize the HTTP service. 46 | */ 47 | bool httpInit(); 48 | /** 49 | * Terminate the HTTP service. 50 | */ 51 | bool httpEnd(); 52 | /** 53 | * Set one of the bearer settings for application based on IP. 54 | */ 55 | bool setBearerSetting(ATConstStr parameter, const char* value); 56 | 57 | public: 58 | SIM808(uint8_t resetPin, uint8_t pwrKeyPin = SIM808_UNAVAILABLE_PIN, uint8_t statusPin = SIM808_UNAVAILABLE_PIN); 59 | ~SIM808(); 60 | 61 | /** 62 | * Get a boolean indicating wether or not the device is currently powered on. 63 | * The power state is read from either the statusPin if set, or from a test AT command response. 64 | */ 65 | bool powered(); 66 | /** 67 | * Power on or off the device only if the requested state is different than the actual state. 68 | * Returns true if the power state has been changed as a result of this call. 69 | * Unavailable and returns false in all cases if pwrKeyPin is not set. 70 | * 71 | * See powered() 72 | */ 73 | bool powerOnOff(bool power); 74 | /** 75 | * Get current charging state, level and voltage from the device. 76 | */ 77 | SIM808ChargingStatus getChargingState(); 78 | 79 | /** 80 | * Get current phone functionality mode. 81 | */ 82 | SIM808PhoneFunctionality getPhoneFunctionality(); 83 | /** 84 | * Set the phone functionality mode. 85 | */ 86 | bool setPhoneFunctionality(SIM808PhoneFunctionality fun); 87 | /** 88 | * Configure slow clock, allowing the device to enter sleep mode. 89 | */ 90 | bool setSlowClock(SIM808SlowClock mode); 91 | 92 | void init(); 93 | void reset(); 94 | 95 | /** 96 | * Send an already formatted command and read a single line response. Useful for unimplemented commands. 97 | */ 98 | size_t sendCommand(const char* cmd, char* response, size_t responseSize); 99 | 100 | bool setEcho(SIM808Echo mode); 101 | /** 102 | * Unlock the SIM card using the provided pin. Beware of failed attempts ! 103 | */ 104 | bool simUnlock(const char* pin); 105 | /** 106 | * Get a string indicating the current sim state. 107 | */ 108 | size_t getSimState(char* state, size_t stateSize); 109 | /** 110 | * Get the device IMEI number. 111 | */ 112 | size_t getImei(char* imei, size_t imeiSize); 113 | 114 | /** 115 | * Get current GSM signal quality, estimated attenuation in dB and error rate. 116 | */ 117 | SIM808SignalQualityReport getSignalQuality(); 118 | 119 | bool setSmsMessageFormat(SIM808SmsMessageFormat format); 120 | /** 121 | * Send a SMS to the provided number. 122 | */ 123 | bool sendSms(const char* addr, const char* msg); 124 | 125 | /** 126 | * Get a boolean indicating wether or not GPRS is currently enabled. 127 | */ 128 | bool getGprsPowerState(bool *state); 129 | /** 130 | * Reinitiliaze and enable GPRS. 131 | */ 132 | bool enableGprs(const char* apn, const char* user = NULL, const char *password = NULL); 133 | /** 134 | * Shutdown GPRS properly. 135 | */ 136 | bool disableGprs(); 137 | /** 138 | * Get the device current network registration status. 139 | */ 140 | SIM808NetworkRegistrationState getNetworkRegistrationStatus(); 141 | 142 | /** 143 | * Get a boolean indicating wether or not GPS is currently powered on. 144 | */ 145 | bool getGpsPowerState(bool *state); 146 | /** 147 | * Power on or off the gps only if the requested state is different than the actual state. 148 | * Returns true if the power state has been changed has a result of this call. 149 | */ 150 | bool powerOnOffGps(bool power); 151 | /** 152 | * Get the latest GPS parsed sequence and a value indicating the current 153 | * fix status. 154 | * Response is only filled if a fix is acquired. 155 | * If a fix is acquired, FIX or ACCURATE_FIX will be returned depending on 156 | * wether or not the satellites used is greater than minSatellitesForAccurateFix. 157 | */ 158 | SIM808GpsStatus getGpsStatus(char * response, size_t responseSize, uint8_t minSatellitesForAccurateFix = GPS_ACCURATE_FIX_MIN_SATELLITES); 159 | /** 160 | * Extract the specified field from the GPS parsed sequence as a uint16_t. 161 | */ 162 | bool getGpsField(const char* response, SIM808GpsField field, uint16_t* result); 163 | /** 164 | * Extract the specified field from the GPS parsed sequence as a float. 165 | */ 166 | bool getGpsField(const char* response, SIM808GpsField field, float* result); 167 | /** 168 | * Return a pointer to the specified field from the GPS parsed sequence. 169 | */ 170 | void getGpsField(const char* response, SIM808GpsField field, char** result); 171 | /** 172 | * Get and return the latest GPS parsed sequence. 173 | */ 174 | bool getGpsPosition(char* response, size_t responseSize); 175 | 176 | 177 | /** 178 | * Send an HTTP GET request and read the server response within the limit of responseSize. 179 | * 180 | * HTTP and HTTPS are supported, based on he provided URL. Note however that HTTPS request 181 | * have a high failure rate that make them unusuable reliably. 182 | */ 183 | uint16_t httpGet(const char* url, char* response, size_t responseSize); 184 | /** 185 | * Send an HTTP POST request and read the server response within the limit of responseSize. 186 | * 187 | * HTTP and HTTPS are supported, based on he provided URL. Note however that HTTPS request 188 | * have a high failure rate that make them unusuable reliably. 189 | */ 190 | uint16_t httpPost(const char* url, ATConstStr contentType, const char* body, char* response, size_t responseSize); 191 | }; 192 | 193 | -------------------------------------------------------------------------------- /src/SIMComAT.Common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #if defined(__AVR__) 6 | typedef const __FlashStringHelper* ATConstStr; 7 | 8 | #define S_PROGMEM PROGMEM 9 | #define S_F(x) F(x) 10 | #define TO_F(x) (reinterpret_cast(x)) 11 | #define TO_P(x) (reinterpret_cast(x)) 12 | #else 13 | typedef const char* ATConstStr; 14 | 15 | #define S_PROGMEM 16 | #define S_F(x) x 17 | #define TO_F(x) x 18 | #define TO_P(x) x 19 | #endif 20 | 21 | #define TOKEN_TEXT(name, text) const char TOKEN_##name[] S_PROGMEM = text 22 | #define TOKEN(name) TOKEN_TEXT(name, #name) 23 | 24 | #define AT_COMMAND(name, text) const char AT_COMMAND_##name[] S_PROGMEM = text 25 | #define AT_COMMAND_PARAMETER(category, name) const char AT_COMMAND_PARAMETER_##category##_##name[] S_PROGMEM = #name 26 | 27 | TOKEN(AT); 28 | TOKEN(OK); 29 | TOKEN(ERROR); 30 | TOKEN_TEXT(READ, "?"); 31 | TOKEN_TEXT(WRITE, "="); 32 | TOKEN_TEXT(NL, "\n"); -------------------------------------------------------------------------------- /src/SIMComAT.cpp: -------------------------------------------------------------------------------- 1 | #include "SIMComAT.h" 2 | #include 3 | 4 | void SIMComAT::begin(Stream& port) 5 | { 6 | _port = &port; 7 | _output.begin(LOG_LEVEL_VERBOSE, this, false); 8 | #if _SIM808_DEBUG 9 | _debug.begin(LOG_LEVEL_VERBOSE, &Serial, false); 10 | #endif // _SIM808_DEBUG 11 | } 12 | 13 | void SIMComAT::flushInput() { 14 | uint16_t timeout = 0; 15 | while(readNext(replyBuffer, BUFFER_SIZE, &timeout)); 16 | memset(replyBuffer, 0, BUFFER_SIZE); 17 | } 18 | 19 | 20 | size_t SIMComAT::readNext(char * buffer, size_t size, uint16_t * timeout, char stop) 21 | { 22 | size_t i = 0; 23 | bool exit = false; 24 | 25 | do { 26 | while(!exit && i < size - 1 && available()) { 27 | char c = read(); 28 | buffer[i] = c; 29 | i++; 30 | 31 | exit |= stop && c == stop; 32 | } 33 | 34 | if(timeout) { 35 | if(*timeout) { 36 | delay(1); 37 | (*timeout)--; 38 | } 39 | 40 | if(!(*timeout)) break; 41 | } 42 | } while(!exit && i < size - 1); 43 | 44 | buffer[i] = '\0'; 45 | 46 | if(i) { 47 | RECEIVEARROW; 48 | SIM808_PRINT(buffer); 49 | } 50 | 51 | return i > 0 ? i - 1 : i; 52 | } 53 | 54 | int8_t SIMComAT::waitResponse(uint16_t timeout, 55 | ATConstStr s1, 56 | ATConstStr s2, 57 | ATConstStr s3, 58 | ATConstStr s4) 59 | { 60 | ATConstStr wantedTokens[4] = { s1, s2, s3, s4 }; 61 | size_t length; 62 | 63 | do { 64 | memset(replyBuffer, 0, BUFFER_SIZE); 65 | length = readNext(replyBuffer, BUFFER_SIZE, &timeout, '\n'); 66 | 67 | if(!length) continue; //read nothing 68 | if(wantedTokens[0] == NULL) return 0; //looking for a line with any content 69 | 70 | for(uint8_t i = 0; i < 4; i++) { 71 | if(wantedTokens[i]) { 72 | char *p = strstr_P(replyBuffer, TO_P(wantedTokens[i])); 73 | if(replyBuffer == p) return i; 74 | } 75 | } 76 | } while(timeout); 77 | 78 | return -1; 79 | } 80 | 81 | size_t SIMComAT::copyCurrentLine(char *dst, size_t dstSize, uint16_t shift) 82 | { 83 | char *p = dst; 84 | char *p1; 85 | 86 | p += safeCopy(replyBuffer + shift, p, dstSize); // copy the current buffer content 87 | // copy the rest of the line if any 88 | if(!strchr(dst, '\n')) p += readNext(p, dstSize - (p - dst), NULL, '\n'); 89 | 90 | // terminating the string no matter what 91 | p1 = strchr(dst, '\n'); 92 | p = p1 ? p1 : p; 93 | *p = '\0'; 94 | 95 | return strlen(dst); 96 | } 97 | 98 | size_t SIMComAT::safeCopy(const char *src, char *dst, size_t dstSize) 99 | { 100 | size_t len = strlen(src); 101 | if (dst != NULL) { 102 | size_t maxLen = min(len + 1, dstSize); 103 | strlcpy(dst, src, maxLen); 104 | } 105 | 106 | return len; 107 | } 108 | 109 | char* SIMComAT::find(const char* str, char divider, uint8_t index) 110 | { 111 | char* p = strchr(str, ':'); 112 | if (p == NULL) p = strchr(str, str[0]); //ditching eventual response header 113 | 114 | p++; 115 | for (uint8_t i = 0; i < index; i++) 116 | { 117 | p = strchr(p, divider); 118 | if (p == NULL) return NULL; 119 | p++; 120 | } 121 | 122 | return p; 123 | } 124 | 125 | bool SIMComAT::parse(const char* str, char divider, uint8_t index, uint8_t* result) 126 | { 127 | uint16_t tmpResult; 128 | if (!parse(str, divider, index, &tmpResult)) return false; 129 | 130 | *result = (uint8_t)tmpResult; 131 | return true; 132 | } 133 | 134 | bool SIMComAT::parse(const char* str, char divider, uint8_t index, int8_t* result) 135 | { 136 | int16_t tmpResult; 137 | if (!parse(str, divider, index, &tmpResult)) return false; 138 | 139 | *result = (int8_t)tmpResult; 140 | return true; 141 | } 142 | 143 | bool SIMComAT::parse(const char* str, char divider, uint8_t index, uint16_t* result) 144 | { 145 | char* p = find(str, divider, index); 146 | if (p == NULL) return false; 147 | 148 | errno = 0; 149 | *result = strtoul(p, NULL, 10); 150 | 151 | return errno == 0; 152 | } 153 | 154 | #if defined(NEED_SIZE_T_OVERLOADS) 155 | bool SIMComAT::parse(const char* str, char divider, uint8_t index, size_t* result) { 156 | char* p = find(str, divider, index); 157 | if (p == NULL) return false; 158 | 159 | errno = 0; 160 | *result = strtoull(p, NULL, 10); 161 | 162 | return errno == 0; 163 | } 164 | #endif 165 | 166 | bool SIMComAT::parse(const char* str, char divider, uint8_t index, int16_t* result) 167 | { 168 | char* p = find(str, divider, index); 169 | if (p == NULL) return false; 170 | 171 | errno = 0; 172 | *result = strtol(p, NULL, 10); 173 | 174 | return errno == 0; 175 | } 176 | 177 | bool SIMComAT::parse(const char* str, char divider, uint8_t index, float* result) 178 | { 179 | char* p = find(str, divider, index); 180 | if (p == NULL) return false; 181 | 182 | errno = 0; 183 | *result = strtod(p, NULL); 184 | 185 | return errno == 0; 186 | } 187 | -------------------------------------------------------------------------------- /src/SIMComAT.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "SIMComAT.Common.h" 6 | 7 | #define _SIM808_DEBUG _DEBUG 8 | 9 | #if _SIM808_DEBUG 10 | #define SIM808_PRINT(...) _debug.verbose(__VA_ARGS__) 11 | #define SIM808_PRINT_CHAR(x) Serial.print((char)x) 12 | #define SIM808_PRINT_P(fmt, ...) _debug.verbose(S_F(fmt "\n"), __VA_ARGS__) 13 | #define SIM808_PRINT_SIMPLE_P(fmt) _debug.verbose(S_F(fmt "\n")) 14 | 15 | #define RECEIVEARROW _debug.verbose(S_F("<--")) 16 | #define SENDARROW _debug.verbose(S_F("\n-->")) 17 | #else 18 | #define SIM808_PRINT(...) 19 | #define SIM808_PRINT_CHAR(x) 20 | #define SIM808_PRINT_P(x, ...) 21 | #define SIM808_PRINT_SIMPLE_P(x) 22 | 23 | #define RECEIVEARROW 24 | #define SENDARROW 25 | #endif // _DEBUG 26 | 27 | #if SIZE_MAX > UINT16_MAX 28 | #define NEED_SIZE_T_OVERLOADS 29 | #endif 30 | 31 | #define BUFFER_SIZE 64 32 | #define SIMCOMAT_DEFAULT_TIMEOUT 1000 33 | 34 | class SIMComAT : public Stream 35 | { 36 | private: 37 | 38 | protected: 39 | Stream* _port; 40 | Logging _output; 41 | #if _SIM808_DEBUG 42 | Logging _debug; 43 | #endif 44 | 45 | char replyBuffer[BUFFER_SIZE]; 46 | 47 | template void writeStream(T last) 48 | { 49 | print(last); 50 | } 51 | 52 | template void writeStream(T head, Args... tail) 53 | { 54 | print(head); 55 | writeStream(tail...); 56 | } 57 | 58 | template void sendAT(Args... cmd) 59 | { 60 | SENDARROW; 61 | writeStream(TO_F(TOKEN_AT), cmd..., TO_F(TOKEN_NL)); 62 | } 63 | 64 | template void sendFormatAT(T format, Args... args) 65 | { 66 | SENDARROW; 67 | writeStream(TO_F(TOKEN_AT)); 68 | _output.verbose(format, args...); 69 | writeStream(TO_F(TOKEN_NL)); 70 | } 71 | 72 | /** 73 | * Read and discard all content already waiting to be parsed. 74 | */ 75 | void flushInput(); 76 | /** 77 | * Read at max size available chars into buffer until either the timeout is exhausted or 78 | * the stop character is encountered. timeout and char are optional 79 | * 80 | */ 81 | size_t readNext(char * buffer, size_t size, uint16_t * timeout = NULL, char stop = 0); 82 | int8_t waitResponse( 83 | ATConstStr s1 = TO_F(TOKEN_OK), 84 | ATConstStr s2 = TO_F(TOKEN_ERROR), 85 | ATConstStr s3 = NULL, 86 | ATConstStr s4 = NULL) { 87 | return waitResponse(SIMCOMAT_DEFAULT_TIMEOUT, s1, s2, s3, s4); 88 | }; 89 | 90 | int8_t waitResponse(uint16_t timeout, 91 | ATConstStr s1 = TO_F(TOKEN_OK), 92 | ATConstStr s2 = TO_F(TOKEN_ERROR), 93 | ATConstStr s3 = NULL, 94 | ATConstStr s4 = NULL); 95 | 96 | /** 97 | * Read the current response line and copy it in response. Start at replyBuffer + shift 98 | */ 99 | size_t copyCurrentLine(char *dst, size_t dstSize, uint16_t shift = 0); 100 | size_t safeCopy(const char *src, char *dst, size_t dstSize); 101 | 102 | /** 103 | * Find and return a pointer to the nth field of a string. 104 | */ 105 | char* find(const char* str, char divider, uint8_t index); 106 | /** 107 | * Parse the nth field of a string as a uint8_t. 108 | */ 109 | bool parse(const char* str, char divider, uint8_t index, uint8_t* result); 110 | /** 111 | * Parse the nth field of a string as a int8_t. 112 | */ 113 | bool parse(const char* str, char divider, uint8_t index, int8_t* result); 114 | /** 115 | * Parse the nth field of a string as a uint16_t. 116 | */ 117 | bool parse(const char* str, char divider, uint8_t index, uint16_t* result); 118 | #if defined(NEED_SIZE_T_OVERLOADS) 119 | /** 120 | * Parse the nth field of a string as a size_t. 121 | */ 122 | bool parse(const char* str, char divider, uint8_t index, size_t* result); 123 | #endif 124 | /** 125 | * Parse the nth field of a string as a int16_t. 126 | */ 127 | bool parse(const char* str, char divider, uint8_t index, int16_t* result); 128 | /** 129 | * Parse the nth field of a string as a float. 130 | */ 131 | bool parse(const char* str, char divider, uint8_t index, float* result); 132 | 133 | /** 134 | * Parse the nth field of the reply buffer as a uint8_t. 135 | */ 136 | bool parseReply(char divider, uint8_t index, uint8_t* result) { return parse(replyBuffer, divider, index, result); } 137 | /** 138 | * Parse the nth field of the reply buffer as a int8_t. 139 | */ 140 | bool parseReply(char divider, uint8_t index, int8_t* result) { return parse(replyBuffer, divider, index, result); } 141 | /** 142 | * Parse the nth field of the reply buffer as a uint16_t. 143 | */ 144 | bool parseReply(char divider, uint8_t index, uint16_t* result) { return parse(replyBuffer, divider, index, result); } 145 | #if defined(NEED_SIZE_T_OVERLOADS) 146 | /** 147 | * Parse the nth field of the reply buffer as a size_t. 148 | */ 149 | bool parseReply(char divider, uint8_t index, size_t* result) { return parse(replyBuffer, divider, index, result); } 150 | #endif 151 | /** 152 | * Parse the nth field of the reply buffer as a int16_t. 153 | */ 154 | bool parseReply(char divider, uint8_t index, int16_t* result) { return parse(replyBuffer, divider, index, result); } 155 | /** 156 | * Parse the nth field of the reply buffer as a float. 157 | */ 158 | bool parseReply(char divider, uint8_t index, float* result) { return parse(replyBuffer, divider, index, result); } 159 | 160 | /** 161 | * Perform the minmum commands needed to get the device communication up and running. 162 | */ 163 | virtual void init()=0; 164 | public: 165 | /** 166 | * Begin communicating with the device. 167 | */ 168 | void begin(Stream& port); 169 | 170 | #pragma region Stream implementation 171 | 172 | int available() { return _port->available(); } 173 | size_t write(uint8_t x) { SIM808_PRINT_CHAR(x); return _port->write(x); } 174 | int read() { return _port->read(); } 175 | int peek() { return _port->peek(); } 176 | void flush() { return _port->flush(); } 177 | 178 | #pragma endregion 179 | 180 | }; --------------------------------------------------------------------------------