diff --git a/examples/c++/CMakeLists.txt b/examples/c++/CMakeLists.txt index ff86c4eb..a9c8b269 100644 --- a/examples/c++/CMakeLists.txt +++ b/examples/c++/CMakeLists.txt @@ -373,3 +373,4 @@ endif() add_custom_example (nmea_gps_i2c_example-cxx nmea_gps_i2c.cxx nmea_gps) add_custom_example (mcp2515-txrx-example-cxx mcp2515-txrx.cxx mcp2515) add_custom_example (ads1015-example-cxx ads1015.cxx ads1x15) +add_custom_example (le910-example-cxx le910.cxx uartat) diff --git a/examples/c++/le910.cxx b/examples/c++/le910.cxx new file mode 100644 index 00000000..e8eebeb2 --- /dev/null +++ b/examples/c++/le910.cxx @@ -0,0 +1,175 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2017 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include "uartat.hpp" +#include "upm_utilities.h" + +using namespace std; + +const size_t bufferLength = 256; + +int main(int argc, char **argv) +{ +//! [Interesting] + + string defaultDev = string("/dev/ttyUSB0"); + if (argc > 1) + defaultDev = string(argv[1]); + + cout << "Using device: " << defaultDev << endl; + + // Instantiate a UARTAT sensor on defaultDev at 115200 baud. + upm::UARTAT *sensor = new upm::UARTAT(defaultDev, 115200); + + // This is a simplistic example that tries to configure the LE910, + // and use it's built-in socket capabilities to connect to a + // remote host, obtain a small piece of data, and return it. It's + // mainly intended to show you how you can use the various AT + // commands supported by the LE910 to perform simple tasks. + // + // You must have a valid SIM card with an active data plan for + // this example to do anything interesting. + // + // See the LE910 AT Commands reference for full information on + // what is possible with this device. The uartat driver is + // intended to make it a little easier to control AT-style + // devices, but is by no means a full-featured communication + // infrastructure. A "real" application will probably need to be + // much more sophisticated with regard to parsing, doing retries, + // etc. + // + // For experimenting with various AT commands, try using an + // interactive terminal emulator like minicom or screen. + + // make sure we are in command mode + if (!sensor->inCommandMode()) + { + cout << "Not in command mode, switching..." << endl; + sensor->commandMode("+++", 1000); + } + + // flter out CR's in responses by default + sensor->filterCR(true); + + cout << "Configuring modem..." << endl; + + // discard any waiting characters + sensor->drain(); + + // reset modem + sensor->command("ATZ\r"); + + // turn off command echo, set verbosity to 1, enable data + // connection mode + sensor->command("ATE0 V1 +FCLASS=0\r"); + sensor->drain(); + + // Now issue some commands and output the results. + + cout << "Modem and SIM information:" << endl; + std::string buffer; + buffer = sensor->commandWithResponse("AT+ICCID\r", bufferLength); + if (!buffer.empty()) + cout << "ICCID (SIM ID): " + << buffer + << endl; + + buffer = sensor->commandWithResponse("AT+CGSN=1\r", bufferLength); + if (!buffer.empty()) + cout << "IMEI: " + << buffer + << endl; + + // see if we are on the network.... + buffer = sensor->commandWithResponse("AT+CREG?\r", bufferLength); + if (!buffer.empty()) + { + cout << buffer << endl; + + // look for "CGREG: 0,1" or "CGREG: 0,5" + if (sensor->find(buffer, "CREG: 0,1") || + sensor->find(buffer, "CREG: 0,5")) + { + cout << "Connected to the cell data network." << endl; + + // wait up to 5 seconds for responses now... + sensor->setResponseWaitTime(5000); + + // setup PDP context (socket 1). An ERROR repsonse is + // possible if the PDP context is already set up. + sensor->command("AT#SGACT=1,1\r"); + + // setup a TCP socket to nist.gov and read the timestamp. + + cout << "Connecting to time-a.nist.gov, TCP port 13" << endl; + + // Wait up to 60 seconds to find the NO CARRIER + // string, which will be present at the end, if the + // connection succeeded and the requested data was + // obtained. + buffer = + sensor->commandWaitFor("AT#SD=1,0,13,\"time-a.nist.gov\"\r", + bufferLength, "\nNO CARRIER\n", 60000); + if (!buffer.empty()) + { + // print out the response + cout << "RESPONSE: " + << endl + << buffer + << endl; + } + else + { + cout << "No response." << endl; + } + + // destroy PDP context + sensor->setResponseWaitTime(250); + sensor->command("AT#SGACT=1,0\r"); + } + else + { + cout << "You do not appear to be connected to the network..." + << endl; + } + } + else + { + cout << "Error executing query\n" << endl; + } + + // reset the modem + sensor->command("ATZ\r"); + + cout << "Exiting" << endl; + + delete sensor; + +//! [Interesting] + + return 0; +} diff --git a/examples/c/CMakeLists.txt b/examples/c/CMakeLists.txt index 6fd7466d..1107c46e 100644 --- a/examples/c/CMakeLists.txt +++ b/examples/c/CMakeLists.txt @@ -154,3 +154,4 @@ add_custom_example (rpr220-intr-example-c rpr220-intr.c rpr220) add_custom_example (md-stepper-example-c md-stepper.c md) add_custom_example (button_intr-example-c button_intr.c button) add_custom_example (mcp2515-txrx-example-c mcp2515-txrx.c mcp2515) +add_custom_example (le910-example-c le910.c uartat) diff --git a/examples/c/le910.c b/examples/c/le910.c new file mode 100644 index 00000000..fa601633 --- /dev/null +++ b/examples/c/le910.c @@ -0,0 +1,169 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2017 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include "uartat.h" +#include "upm_utilities.h" + +const size_t bufferLength = 256; + +int main(int argc, char **argv) +{ +//! [Interesting] + + char *defaultDev = "/dev/ttyUSB0"; + if (argc > 1) + defaultDev = argv[1]; + + printf("Using device: %s\n", defaultDev); + // Instantiate a UARTAT sensor on defaultDev at 115200 baud. + uartat_context sensor = uartat_init_tty(defaultDev, 115200); + + if (!sensor) + { + printf("uartat_init_tty() failed.\n"); + return 1; + } + + // This is a simplistic example that tries to configure the LE910, + // and use it's built-in socket capabilities to connect to a + // remote host, obtain a small piece of data, and return it. It's + // mainly intended to show you how you can use the various AT + // commands supported by the LE910 to perform simple tasks. + // + // You must have a valid SIM card with an active data plan for + // this example to do anything interesting. + // + // See the LE910 AT Commands reference for full information on + // what is possible with this device. The uartat driver is + // intended to make it a little easier to control AT-style + // devices, but is by no means a full-featured communication + // infrastructure. A "real" application will probably need to be + // much more sophisticated, and custom designed for the desired + // task. + // + // For experimenting with various AT commands, try using an + // interactive terminal emulator like minicom or screen. + + char buffer[bufferLength]; + + // make sure we are in command mode + if (!uartat_in_command_mode(sensor)) + { + printf("Not in command mode, switching...\n"); + uartat_command_mode(sensor, "+++", 1000); + } + + // flter out CR's in responses by default + uartat_filter_cr(sensor, true); + + printf("Configuring modem...\n"); + + // discard any waiting characters + uartat_drain(sensor); + + // reset modem + uartat_command(sensor, "ATZ\r"); + + // turn off command echo, set verbosity to 1, enable data + // connection mode + uartat_command(sensor, "ATE0 V1 +FCLASS=0\r"); + uartat_drain(sensor); + + // Now issue some commands and output the results. + + printf("Modem and SIM information:\n"); + if (uartat_command_with_response(sensor, "AT+ICCID\r", + buffer, bufferLength)) + printf("ICCID (SIM ID): %s\n", buffer); + + if (uartat_command_with_response(sensor, "AT+CGSN=1\r", + buffer, bufferLength)) + printf("IMEI: %s\n", buffer); + + // see if we are on the network.... + if (uartat_command_with_response(sensor, "AT+CREG?\r", + buffer, bufferLength)) + { + printf("%s\n", buffer); + // look for "CGREG: 0,1" or "CGREG: 0,5" + if (uartat_find(sensor, buffer, "CREG: 0,1") || + uartat_find(sensor, buffer, "CREG: 0,5")) + { + printf("Connected to the cell data network.\n"); + + // wait up to 5 seconds for responses now... + uartat_set_response_wait_time(sensor, 5000); + + // setup PDP context (socket 1). An ERROR response is + // possible if the PDP context is already set up. + uartat_command(sensor, "AT#SGACT=1,1\r"); + + // setup a TCP socket to nist.gov and read the timestamp. + + printf("Connecting to time-a.nist.gov, TCP port 13\n"); + + // Wait up to 60 seconds to find the NO CARRIER + // string, which will be present at the end, if the + // connection succeeded and the requested data was + // obtained. + if (uartat_command_waitfor(sensor, + "AT#SD=1,0,13,\"time-a.nist.gov\"\r", + buffer, bufferLength, + "\nNO CARRIER\n", 60000)) + { + // print out the response + printf("RESPONSE:\r%s\r", buffer); + } + else + { + printf("No response.\n"); + } + + // destroy PDP context + uartat_set_response_wait_time(sensor, 250); + uartat_command(sensor, "AT#SGACT=1,0\r"); + } + else + { + printf("You do not appear to be connected to the network...\n"); + } + } + else + { + printf("Error executing query\n"); + } + + // reset the modem + uartat_command(sensor, "ATZ\r"); + + printf("Exiting\n"); + + uartat_close(sensor); + +//! [Interesting] + + return 0; +} diff --git a/examples/java/CMakeLists.txt b/examples/java/CMakeLists.txt index dbceccf8..fe248c70 100644 --- a/examples/java/CMakeLists.txt +++ b/examples/java/CMakeLists.txt @@ -185,3 +185,4 @@ if (OPENZWAVE_FOUND) endif() add_example_with_path(NMEAGPS_I2C_Example nmea_gps nmea_gps) add_example_with_path(MCP2515_TXRX_Example mcp2515 mcp2515) +add_example_with_path(LE910_Example uartat uartat) diff --git a/examples/java/LE910_Example.java b/examples/java/LE910_Example.java new file mode 100644 index 00000000..d17cecaa --- /dev/null +++ b/examples/java/LE910_Example.java @@ -0,0 +1,167 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2017 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import upm_uartat.UARTAT; + +public class LE910_Example +{ + private static String defaultDev = "/dev/ttyUSB0"; + static private final int bufferLength = 256; + + public static void main(String[] args) throws InterruptedException + { +// ! [Interesting] + if (args.length > 0) + defaultDev = args[0]; + + System.out.println("Using device " + defaultDev); + System.out.println("Initializing..."); + + // Instantiate a UARTAT sensor on defaultDev at 115200 baud. + UARTAT sensor = new UARTAT(defaultDev, 115200); + + // This is a simplistic example that tries to configure the LE910, + // and use it's built-in socket capabilities to connect to a + // remote host, obtain a small piece of data, and return it. It's + // mainly intended to show you how you can use the various AT + // commands supported by the LE910 to perform simple tasks. + // + // You must have a valid SIM card with an active data plan for + // this example to do anything interesting. + // + // See the LE910 AT Commands reference for full information on + // what is possible with this device. The uartat driver is + // intended to make it a little easier to control AT-style + // devices, but is by no means a full-featured communication + // infrastructure. A "real" application will probably need to be + // much more sophisticated with regard to parsing, doing retries, + // etc. + // + // For experimenting with various AT commands, try using an + // interactive terminal emulator like minicom or screen. + + // make sure we are in command mode + if (!sensor.inCommandMode()) + { + System.out.println("Not in command mode, switching..."); + sensor.commandMode("+++", 1000); + } + + // flter out CR's in responses by default + sensor.filterCR(true); + + System.out.println("Configuring modem..."); + + // discard any waiting characters + sensor.drain(); + + // reset modem + sensor.command("ATZ\r"); + + // turn off command echo, set verbosity to 1, enable data + // connection mode + sensor.command("ATE0 V1 +FCLASS=0\r"); + sensor.drain(); + + // Now issue some commands and output the results. + + System.out.println("Modem and SIM information:"); + + String buffer; + + buffer = sensor.commandWithResponse("AT+ICCID\r", bufferLength); + if (buffer.length() > 0) + { + System.out.println("ICCID (SIM ID): " + buffer); + } + + buffer = sensor.commandWithResponse("AT+CGSN=1\r", bufferLength); + if (buffer.length() > 0) + { + System.out.println("IMEI: " + buffer); + } + + // see if we are on the network.... + buffer = sensor.commandWithResponse("AT+CREG?\r", bufferLength); + if (buffer.length() > 0) + { + System.out.println(buffer); + + // look for "CGREG: 0,1" or "CGREG: 0,5" + if (sensor.find(buffer, "CREG: 0,1") || + sensor.find(buffer, "CREG: 0,5")) + { + System.out.println("Connected to the cell data network."); + + // wait up to 5 seconds for responses now... + sensor.setResponseWaitTime(5000); + + // setup PDP context (socket 1). An ERROR repsonse is + // possible if the PDP context is already set up. + sensor.command("AT#SGACT=1,1\r"); + + // setup a TCP socket to nist.gov and read the timestamp. + + System.out.println("Connecting to time-a.nist.gov, TCP port 13"); + + // Wait up to 60 seconds to find the NO CARRIER + // string, which will be present at the end, if the + // connection succeeded and the requested data was + // obtained. + buffer = + sensor.commandWaitFor("AT#SD=1,0,13,\"time-a.nist.gov\"\r", + bufferLength, "\nNO CARRIER\n", 60000); + if (buffer.length() > 0) + { + // print out the response + System.out.println("RESPONSE: "); + System.out.println(buffer); + } + else + { + System.out.println("No response."); + } + + // destroy PDP context + sensor.setResponseWaitTime(250); + sensor.command("AT#SGACT=1,0\r"); + } + else + { + System.out.println("You do not appear to be connected to the network..."); + } + } + else + { + System.out.println("Error executing query\n"); + } + + // reset the modem + sensor.command("ATZ\r"); + + System.out.println("Exiting"); + +// ! [Interesting] + } +} diff --git a/examples/javascript/le910.js b/examples/javascript/le910.js new file mode 100644 index 00000000..05f39e4c --- /dev/null +++ b/examples/javascript/le910.js @@ -0,0 +1,166 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2017 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +var sensorObj = require('jsupm_uartat'); + +var defaultDev = "/dev/ttyUSB0"; + +// if an argument was specified, use it as the device instead +if (process.argv.length > 2) +{ + defaultDev = process.argv[2]; +} + +console.log("Using device:", defaultDev); +console.log("Initializing..."); + +// Instantiate a UARTAT sensor on defaultDev at 115200 baud. +var sensor = new sensorObj.UARTAT(defaultDev, 115200); + +// This is a simplistic example that tries to configure the LE910, +// and use it's built-in socket capabilities to connect to a +// remote host, obtain a small piece of data, and return it. It's +// mainly intended to show you how you can use the various AT +// commands supported by the LE910 to perform simple tasks. +// +// You must have a valid SIM card with an active data plan for +// this example to do anything interesting. +// +// See the LE910 AT Commands reference for full information on +// what is possible with this device. The uartat driver is +// intended to make it a little easier to control AT-style +// devices, but is by no means a full-featured communication +// infrastructure. A "real" application will probably need to be +// much more sophisticated with regard to parsing, doing retries, +// etc. +// +// For experimenting with various AT commands, try using an +// interactive terminal emulator like minicom or screen. + +// make sure we are in command mode +if (!sensor.inCommandMode()) +{ + console.log("Not in command mode, switching..."); + sensor.commandMode("+++", 1000); +} + +// flter out CR's in responses by default +sensor.filterCR(true); + +console.log("Configuring modem..."); + +// discard any waiting characters +sensor.drain(); + +// reset modem +sensor.command("ATZ\r"); + +// turn off command echo, set verbosity to 1, enable data +// connection mode +sensor.command("ATE0 V1 +FCLASS=0\r"); +sensor.drain(); + +// Now issue some commands and output the results. + +console.log("Modem and SIM information:"); + +var buffer; +var bufferLength = 256; + +buffer = sensor.commandWithResponse("AT+ICCID\r", bufferLength); +if (buffer.length > 0) +{ + console.log("ICCID (SIM ID):", buffer); +} + +buffer = sensor.commandWithResponse("AT+CGSN=1\r", bufferLength); +if (buffer.length > 0) +{ + console.log("IMEI: ", buffer); +} + +// see if we are on the network.... +buffer = sensor.commandWithResponse("AT+CREG?\r", bufferLength); +if (buffer.length > 0) +{ + console.log(buffer); + + // look for "CGREG: 0,1" or "CGREG: 0,5" + if (sensor.find(buffer, "CREG: 0,1") || + sensor.find(buffer, "CREG: 0,5")) + { + console.log("Connected to the cell data network."); + + // wait up to 5 seconds for responses now... + sensor.setResponseWaitTime(5000); + + // setup PDP context (socket 1). An ERROR repsonse is + // possible if the PDP context is already set up. + sensor.command("AT#SGACT=1,1\r"); + + // setup a TCP socket to nist.gov and read the timestamp. + + console.log("Connecting to time-a.nist.gov, TCP port 13"); + + // Wait up to 60 seconds to find the NO CARRIER + // string, which will be present at the end, if the + // connection succeeded and the requested data was + // obtained. + buffer = + sensor.commandWaitFor("AT#SD=1,0,13,\"time-a.nist.gov\"\r", + bufferLength, "\nNO CARRIER\n", 60000); + if (buffer.length > 0) + { + // print out the response + console.log("RESPONSE: "); + console.log(buffer); + } + else + { + console.log("No response."); + } + + // destroy PDP context + sensor.setResponseWaitTime(250); + sensor.command("AT#SGACT=1,0\r"); + } + else + { + console.log("You do not appear to be connected to the network..."); + } +} +else +{ + console.log("Error executing query\n"); +} + +// reset the modem +sensor.command("ATZ\r"); + +console.log("Exiting"); + +sensor = null; +sensorObj.cleanUp(); +sensorObj = null; +process.exit(0); diff --git a/examples/python/le910.py b/examples/python/le910.py new file mode 100755 index 00000000..3d021e54 --- /dev/null +++ b/examples/python/le910.py @@ -0,0 +1,157 @@ +#!/usr/bin/python +# Author: Jon Trulson +# Copyright (c) 2017 Intel Corporation. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +from __future__ import print_function +import time, sys, signal, atexit +from upm import pyupm_uartat as UARTAT + +def main(): + ## Exit handlers ## + # This function stops python from printing a stacktrace when you + # hit control-C + def SIGINTHandler(signum, frame): + raise SystemExit + + # This function lets you run code on exit, including functions from sensor + def exitHandler(): + print("Exiting") + sys.exit(0) + + # Register exit handlers + atexit.register(exitHandler) + signal.signal(signal.SIGINT, SIGINTHandler) + + defaultDev = "/dev/ttyUSB0" + + # if an argument was specified, use it as the device instead + if (len(sys.argv) > 1): + defaultDev = sys.argv[1] + + print("Using device:", defaultDev); + + # Instantiate a UARTAT sensor on defaultDev at 115200 baud. + sensor = UARTAT.UARTAT(defaultDev, 115200) + + # This is a simplistic example that tries to configure the LE910, + # and use it's built-in socket capabilities to connect to a + # remote host, obtain a small piece of data, and return it. It's + # mainly intended to show you how you can use the various AT + # commands supported by the LE910 to perform simple tasks. + # + # You must have a valid SIM card with an active data plan for + # this example to do anything interesting. + # + # See the LE910 AT Commands reference for full information on + # what is possible with this device. The uartat driver is + # intended to make it a little easier to control AT-style + # devices, but is by no means a full-featured communication + # infrastructure. A "real" application will probably need to be + # much more sophisticated with regard to parsing, doing retries, + # etc. + # + # For experimenting with various AT commands, try using an + # interactive terminal emulator like minicom or screen. + + # make sure we are in command mode + if (not sensor.inCommandMode()): + print("Not in command mode, switching...") + sensor.commandMode("+++", 1000) + + # flter out CR's in responses by default + sensor.filterCR(True) + + print("Configuring modem...") + + # discard any waiting characters + sensor.drain() + + # reset modem + sensor.command("ATZ\r") + + # turn off command echo, set verbosity to 1, enable data + # connection mode + sensor.command("ATE0 V1 +FCLASS=0\r") + sensor.drain() + + # Now issue some commands and output the results. + + print("Modem and SIM information:") + + bufferLength = 256 + + buffer = sensor.commandWithResponse("AT+ICCID\r", bufferLength) + if (buffer): + print("ICCID (SIM ID):", buffer) + + buffer = sensor.commandWithResponse("AT+CGSN=1\r", bufferLength) + if (buffer): + print("IMEI: ", buffer) + + # see if we are on the network.... + buffer = sensor.commandWithResponse("AT+CREG?\r", bufferLength) + if (buffer): + print(buffer) + + # look for "CGREG: 0,1" or "CGREG: 0,5" + if (sensor.find(buffer, "CREG: 0,1") or + sensor.find(buffer, "CREG: 0,5")): + print("Connected to the cell data network.") + + # wait up to 5 seconds for responses now... + sensor.setResponseWaitTime(5000) + + # setup PDP context (socket 1). An ERROR repsonse is + # possible if the PDP context is already set up. + sensor.command("AT#SGACT=1,1\r") + + # setup a TCP socket to nist.gov and read the timestamp. + + print("Connecting to time-a.nist.gov, TCP port 13") + + # Wait up to 60 seconds to find the NO CARRIER + # string, which will be present at the end, if the + # connection succeeded and the requested data was + # obtained. + buffer = sensor.commandWaitFor("AT#SD=1,0,13,\"time-a.nist.gov\"\r", + bufferLength, "\nNO CARRIER\n", 60000) + if (buffer): + # print out the response + print("RESPONSE: ") + print(buffer) + else: + print("No response.") + + # destroy PDP context + sensor.setResponseWaitTime(250) + sensor.command("AT#SGACT=1,0\r") + else: + print("You do not appear to be connected to the network...") + else: + print("Error executing query\n") + + # reset the modem + sensor.command("ATZ\r") + + +if __name__ == '__main__': + main() diff --git a/src/uartat/CMakeLists.txt b/src/uartat/CMakeLists.txt new file mode 100644 index 00000000..45cc6524 --- /dev/null +++ b/src/uartat/CMakeLists.txt @@ -0,0 +1,8 @@ +upm_mixed_module_init (NAME uartat + DESCRIPTION "Generic UART driver for serial AT command driven devices" + C_HDR uartat.h uartat_defs.h + C_SRC uartat.c + CPP_HDR uartat.hpp + CPP_SRC uartat.cxx + CPP_WRAPS_C + REQUIRES mraa) diff --git a/src/uartat/javaupm_uartat.i b/src/uartat/javaupm_uartat.i new file mode 100644 index 00000000..8b36f4f1 --- /dev/null +++ b/src/uartat/javaupm_uartat.i @@ -0,0 +1,21 @@ +%module javaupm_uartat +%include "../upm.i" +%include "std_string.i" +%include "stdint.i" +%include "typemaps.i" + +%include "uartat.hpp" +%{ + #include "uartat.hpp" +%} + +%pragma(java) jniclasscode=%{ + static { + try { + System.loadLibrary("javaupm_uartat"); + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library failed to load. \n" + e); + System.exit(1); + } + } +%} diff --git a/src/uartat/jsupm_uartat.i b/src/uartat/jsupm_uartat.i new file mode 100644 index 00000000..5984e369 --- /dev/null +++ b/src/uartat/jsupm_uartat.i @@ -0,0 +1,8 @@ +%module jsupm_uartat +%include "../upm.i" +%include "std_string.i" + +%include "uartat.hpp" +%{ + #include "uartat.hpp" +%} diff --git a/src/uartat/pyupm_uartat.i b/src/uartat/pyupm_uartat.i new file mode 100644 index 00000000..b61de52b --- /dev/null +++ b/src/uartat/pyupm_uartat.i @@ -0,0 +1,12 @@ +// Include doxygen-generated documentation +%include "pyupm_doxy2swig.i" +%module pyupm_uartat +%include "../upm.i" +%include "std_string.i" + +%feature("autodoc", "3"); + +%include "uartat.hpp" +%{ + #include "uartat.hpp" +%} diff --git a/src/uartat/uartat.c b/src/uartat/uartat.c new file mode 100644 index 00000000..5597504b --- /dev/null +++ b/src/uartat/uartat.c @@ -0,0 +1,412 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2017 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include "uartat.h" + +#include "upm_utilities.h" + +#define UARTAT_MAX_BUFFER (1024) + +// milliseconds +#define UARTAT_MAX_WAIT (1000) + +// milliseconds +#define UARTAT_DEFAULT_RESP_DELAY (250) + +static uartat_context _uartat_preinit() +{ + // make sure MRAA is initialized + int mraa_rv; + if ((mraa_rv = mraa_init()) != MRAA_SUCCESS) + { + printf("%s: mraa_init() failed (%d).\n", __FUNCTION__, mraa_rv); + return NULL; + } + + uartat_context dev = + (uartat_context)malloc(sizeof(struct _uartat_context)); + + if (!dev) + return NULL; + + // zero out context + memset((void *)dev, 0, sizeof(struct _uartat_context)); + + dev->cmd_resp_wait_ms = UARTAT_DEFAULT_RESP_DELAY; + + return dev; +} + +static uartat_context _uartat_postinit(uartat_context dev, + unsigned int baudrate) +{ + assert(dev != NULL); + + if (uartat_set_baudrate(dev, baudrate)) + { + printf("%s: uartat_set_baudrate() failed.\n", __FUNCTION__); + uartat_close(dev); + return NULL; + } + + if (uartat_set_flow_control(dev, UARTAT_FLOW_CONTROL_NONE)) + { + printf("%s: uartat_set_flow_control() failed.\n", __FUNCTION__); + uartat_close(dev); + return NULL; + } + + return dev; +} + +// uart init +uartat_context uartat_init(unsigned int uart, unsigned int baudrate) +{ + uartat_context dev; + + if (!(dev = _uartat_preinit())) + return NULL; + + // initialize the MRAA context + + // uart, default should be 8N1 + if (!(dev->uart = mraa_uart_init(uart))) + { + printf("%s: mraa_uart_init() failed.\n", __FUNCTION__); + uartat_close(dev); + return NULL; + } + + return _uartat_postinit(dev, baudrate); +} + +// uart tty init +uartat_context uartat_init_tty(const char *uart_tty, unsigned int baudrate) +{ + uartat_context dev; + + if (!(dev = _uartat_preinit())) + return NULL; + + // initialize the MRAA context + + // uart, default should be 8N1 + if (!(dev->uart = mraa_uart_init_raw(uart_tty))) + { + printf("%s: mraa_uart_init_raw() failed.\n", __FUNCTION__); + uartat_close(dev); + return NULL; + } + + return _uartat_postinit(dev, baudrate); +} + +void uartat_close(uartat_context dev) +{ + assert(dev != NULL); + + if (dev->uart) + mraa_uart_stop(dev->uart); + + free(dev); +} + +int uartat_read(const uartat_context dev, char *buffer, size_t len) +{ + assert(dev != NULL); + + // uart + return mraa_uart_read(dev->uart, buffer, len); +} + +int uartat_write(const uartat_context dev, const char *buffer, size_t len) +{ + assert(dev != NULL); + + int rv = mraa_uart_write(dev->uart, buffer, len); + mraa_uart_flush(dev->uart); + + return rv; +} + +bool uartat_data_available(const uartat_context dev, unsigned int millis) +{ + assert(dev != NULL); + + if (mraa_uart_data_available(dev->uart, millis)) + return true; + else + return false; +} + +upm_result_t uartat_set_baudrate(const uartat_context dev, + unsigned int baudrate) +{ + assert(dev != NULL); + + if (mraa_uart_set_baudrate(dev->uart, baudrate)) + { + printf("%s: mraa_uart_set_baudrate() failed.\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + return UPM_SUCCESS; +} + +void uartat_set_response_wait_time(const uartat_context dev, + unsigned int wait_time) +{ + assert(dev != NULL); + + dev->cmd_resp_wait_ms = wait_time; +} + +bool uartat_command_mode(const uartat_context dev, const char *cmd_chars, + unsigned int guard_ms) +{ + assert(dev != NULL); + + uartat_drain(dev); + + upm_delay_ms(guard_ms); + + uartat_write(dev, cmd_chars, strlen(cmd_chars)); + + upm_delay_ms(guard_ms); + + char resp[UARTAT_MAX_BUFFER]; + if (uartat_data_available(dev, UARTAT_MAX_WAIT)) + { + int rv = uartat_read(dev, resp, UARTAT_MAX_BUFFER); + + if (rv > 0 && (strstr(resp, "OK") || strstr(resp, "0"))) + return true; + } + + return false; +} + +bool uartat_in_command_mode(const uartat_context dev) +{ + assert(dev != NULL); + + static int buflen = 32; + char buffer[buflen]; + + if (uartat_command_with_response(dev, "AT\r", buffer, buflen) <= 0) + return false; + + // depending on verbosity, you may get "OK" or "0". Try to catch both. + if (strstr(buffer, "OK") || strstr(buffer, "0")) + return true; + else + return false; +} + +void uartat_drain(const uartat_context dev) +{ + assert(dev != NULL); + + char resp[UARTAT_MAX_BUFFER]; + int rv; + while (uartat_data_available(dev, 0)) + { + rv = uartat_read(dev, resp, UARTAT_MAX_BUFFER); + if (rv < 0) + { + printf("%s: read failed\n", __FUNCTION__); + return; + } + // printf("%s: Tossed %d bytes\n", __FUNCTION__, rv); + } + + return; +} + +int uartat_command_with_response(const uartat_context dev, + const char *cmd, + char *resp, size_t resp_len) +{ + assert(dev != NULL); + assert(cmd != NULL); + + uartat_drain(dev); + if (uartat_write(dev, cmd, strlen(cmd)) < 0) + { + printf("%s: uartat_write failed\n", __FUNCTION__); + return -1; + } + + if (resp && resp_len > 1) + { + memset(resp, 0, resp_len); + + upm_clock_t clock; + upm_clock_init(&clock); + + int idx = 0; + + do + { + if (uartat_data_available(dev, 1)) + { + int rv = uartat_read(dev, &resp[idx], 1); + + if (rv < 0) + return rv; + + if (dev->filter_cr && resp[idx] == '\r') + continue; + + if (idx >= resp_len - 1) + return idx; + + idx++; + } + } while (upm_elapsed_ms(&clock) < dev->cmd_resp_wait_ms); + + return idx; + } + else + { + upm_delay_ms(dev->cmd_resp_wait_ms); + uartat_drain(dev); + } + + return 0; +} + +bool uartat_command_waitfor(const uartat_context dev, const char *cmd, + char *resp, size_t resp_len, + const char *wait_string, + unsigned int millis) +{ + assert(dev != NULL); + assert(cmd != NULL); + assert(resp != NULL); + assert(resp_len > 0); + assert(wait_string != NULL); + + uartat_drain(dev); + if (uartat_write(dev, cmd, strlen(cmd)) < 0) + { + printf("%s: uartat_write failed\n", __FUNCTION__); + return -1; + } + + memset(resp, 0, resp_len); + + upm_clock_t clock; + upm_clock_init(&clock); + + int idx = 0; + + do + { + if (uartat_data_available(dev, 1)) + { + int rv = uartat_read(dev, &resp[idx], 1); + + if (rv < 0) + return false; + + if (dev->filter_cr && resp[idx] == '\r') + continue; + + if (idx >= resp_len - 1) + { + // one last check + if (uartat_find(dev, resp, wait_string)) + return true; + else + return false; + } + idx++; + + // see if the string is present + if (uartat_find(dev, resp, wait_string)) + return true; + } + } while (upm_elapsed_ms(&clock) < millis); + + return false; +} + +void uartat_command(const uartat_context dev, const char *cmd) +{ + assert(dev != NULL); + assert(cmd != NULL); + + uartat_command_with_response(dev, cmd, NULL, 0); +} + +upm_result_t uartat_set_flow_control(const uartat_context dev, + UARTAT_FLOW_CONTROL_T fc) +{ + assert(dev != NULL); + + mraa_result_t rv = MRAA_SUCCESS; + + switch(fc) + { + case UARTAT_FLOW_CONTROL_NONE: + rv = mraa_uart_set_flowcontrol(dev->uart, false, false); + break; + + case UARTAT_FLOW_CONTROL_HARD: + rv = mraa_uart_set_flowcontrol(dev->uart, false, true); + break; + + case UARTAT_FLOW_CONTROL_SOFT: + rv = mraa_uart_set_flowcontrol(dev->uart, true, false); + break; + + default: + return UPM_ERROR_INVALID_PARAMETER; + } + + if (rv == MRAA_SUCCESS) + return UPM_SUCCESS; + else + return UPM_ERROR_OPERATION_FAILED; +} + +bool uartat_find(const uartat_context dev, const char *buffer, const char *str) +{ + assert(dev != NULL); + assert(buffer != NULL); + assert(str != NULL); + + return ((strstr(buffer, str)) ? true : false); +} + +void uartat_filter_cr(const uartat_context dev, bool enable) +{ + assert(dev != NULL); + + dev->filter_cr = enable; +} diff --git a/src/uartat/uartat.cxx b/src/uartat/uartat.cxx new file mode 100644 index 00000000..9d4d99c4 --- /dev/null +++ b/src/uartat/uartat.cxx @@ -0,0 +1,173 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2017 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +#include "uartat.hpp" + +using namespace upm; +using namespace std; + +UARTAT::UARTAT(unsigned int uart, unsigned int baudrate) : + m_uartat(uartat_init(uart, baudrate)) +{ + if (!m_uartat) + throw std::runtime_error(string(__FUNCTION__) + + ": uartat_init() failed"); +} + +UARTAT::UARTAT(string uart_path, unsigned int baudrate) : + m_uartat(uartat_init_tty(uart_path.c_str(), baudrate)) +{ + if (!m_uartat) + throw std::runtime_error(string(__FUNCTION__) + + ": uartat_init_tty() failed"); +} + +UARTAT::~UARTAT() +{ + uartat_close(m_uartat); +} + +std::string UARTAT::readStr(size_t size) +{ + char buffer[size]; + + int rv; + + if ((rv = uartat_read(m_uartat, buffer, size)) < 0) + throw std::runtime_error(string(__FUNCTION__) + + ": uartat_read() failed"); + + return string(buffer, rv); +} + +int UARTAT::writeStr(std::string buffer) +{ + int rv; + + if ((rv = uartat_write(m_uartat, (char*)buffer.data(), + buffer.size())) < 0) + throw std::runtime_error(string(__FUNCTION__) + + ": uartat_write() failed"); + + return rv; +} + +void UARTAT::setBaudrate(unsigned int baudrate) +{ + if (uartat_set_baudrate(m_uartat, baudrate)) + throw std::runtime_error(string(__FUNCTION__) + + ": uartat_baudrate() failed"); +} + +void UARTAT::setResponseWaitTime(unsigned int wait_time) +{ + uartat_set_response_wait_time(m_uartat, wait_time); +} + +bool UARTAT::dataAvailable(unsigned int millis) +{ + return uartat_data_available(m_uartat, millis); +} + +bool UARTAT::commandMode(std::string cmd_chars, unsigned int guard_ms) +{ + return uartat_command_mode(m_uartat, cmd_chars.c_str(), guard_ms); +} + +bool UARTAT::inCommandMode() +{ + return uartat_in_command_mode(m_uartat); +} + +void UARTAT::drain() +{ + uartat_drain(m_uartat); + return; +} + +string UARTAT::commandWithResponse(const string cmd, size_t resp_len) +{ + char buffer[resp_len]; + + int rv; + + if ((rv = uartat_command_with_response(m_uartat, cmd.c_str(), buffer, + resp_len)) < 0) + throw std::runtime_error(string(__FUNCTION__) + + ": uartat_command_with_response() failed"); + + return string(buffer, rv); +} + +string UARTAT::commandWaitFor(const std::string cmd, size_t resp_len, + const std::string waitString, + unsigned int millis) +{ + char buffer[resp_len]; + + if (uartat_command_waitfor(m_uartat, cmd.c_str(), buffer, resp_len, + waitString.c_str(), millis)) + return string(buffer, strlen(buffer)); + else + return string(""); +} + +void UARTAT::command(const string cmd) +{ + uartat_command(m_uartat, cmd.c_str()); + + return; +} + +string UARTAT::stringCR2LF(string str) +{ + for (size_t i=0; i + * Copyright (c) 2017 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include + +#include +#include + +#include "uartat_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * @file uartat.h + * @library uartat + * @brief Generic API for AT command based UART devices + * + */ + + /** + * Device context + */ + typedef struct _uartat_context { + mraa_uart_context uart; + + // wait time for reading results after sending a command. The + // default is 250ms. + unsigned int cmd_resp_wait_ms; + + // filter carriage returns (CR) out of responses? + bool filter_cr; + } *uartat_context; + + /** + * UARTAT Initializer for generic UART operation using a UART index. + * + * @param uart Specify which uart to use. + * @param baudrate Specify the baudrate to use. + * @return an initialized device context on success, NULL on error. + */ + uartat_context uartat_init(unsigned int uart, unsigned int baudrate); + + /** + * UARTAT Initializer for generic UART operation using a filesystem + * tty path (eg. /dev/ttyUSB0). + * + * @param uart_tty character string representing a filesystem path to a + * serial tty device. + * @param baudrate Specify the baudrate to use. + * @return an initialized device context on success, NULL on error. + */ + uartat_context uartat_init_tty(const char *uart_tty, unsigned int baudrate); + + /** + * UARTAT sensor close function + * + * @param dev Device context + */ + void uartat_close(uartat_context dev); + + /** + * Read character data from the device. + * + * @param dev Device context + * @param buffer The character buffer to read data into. + * @param len The maximum size of the buffer + * @return The number of bytes successfully read, or -1 on error + */ + int uartat_read(const uartat_context dev, char *buffer, size_t len); + + /** + * Write character data to the device. + * + * @param dev Device context + * @param buffer The character buffer containing data to write. + * @param len The number of bytes to write. + * @return The number of bytes successfully written, or -1 on error. + */ + int uartat_write(const uartat_context dev, const char *buffer, size_t len); + + /** + * Set the baudrate of the device. + * + * @param dev Device context + * @param baudrate The baud rate to set for the device. + * @return UPM result + */ + upm_result_t uartat_set_baudrate(const uartat_context dev, + unsigned int baudrate); + + /** + * Set the default time, in milliseconds, to wait for data to + * arrive after sending a command. + * + * @param dev Device context + * @param wait_ms The response delay to set, in milliseconds. + */ + void uartat_set_response_wait_time(const uartat_context dev, + unsigned int wait_ms); + + /** + * Determine whether there is data available to be read. This + * function will wait up to "millis" milliseconds for data to + * become available. + * + * @param dev Device context + * @param millis The number of milliseconds to wait for data to + * become available. + * @return true if data is available to be read, false otherwise. + */ + bool uartat_data_available(const uartat_context dev, + unsigned int millis); + + /** + * Place the device in AT command mode. Many devices operate in a + * transparent mode and an AT command mode. Command mode is + * required to issue AT based commands. When in transparent mode, + * the device will usually listen for a special sequence of + * characters and delays, indicating that AT command mode should + * be entered. + * + * On most devices, the sequence is: + * +++ + * + * For most devices, the wait time is 1 second (1000 ms) and the + * character sequence is "+++". These options can often be + * configured on the device. + * + * This function will wait millis milliseconds, write the command + * characters (typically "+++"), then wait millis milliseconds again. + * At this time a read will be attempted, looking for the "OK" + * response indicating command mode was successfully entered. + * + * @param dev Device context + * @param cmd_chars The character sequence to write, typically "+++". + * @param guard_ms The number of milliseconds to delay before and + * after the cmd_chars are written. + * @return true if AT command mode ("OK" detected) was + * successfully entered, false otherwise. + */ + bool uartat_command_mode(const uartat_context dev, const char *cmd_chars, + unsigned int guard_ms); + + /** + * Check to see if the device is in command mode. This is + * accomplished by sending an "AT\r" command and seeing if "OK" or + * "0" is returned. + * + * @param dev Device context + * @return true if AT command mode was detected, false otherwise. + */ + bool uartat_in_command_mode(const uartat_context dev); + + /** + * Read and throw away any data currently available to be read. + * This is useful to avoid reading data that might have been the + * result of a previous command interfering with data you + * currently want to read. This function is automatically called + * by uartat_command_with_response(), uartat_command(), and + * uartat_command_waitfor() prior to writing the requested command + * to the device. + * + * @param dev Device context + */ + void uartat_drain(const uartat_context dev); + + /** + * Send an AT command and optionally return a response. + * + * @param dev Device context + * @param cmd A character string containing the AT command to + * send, including the "AT" prefix and a terminating carriage + * return ("\r"). + * @param resp A pointer to a buffer that will contain the + * response. If NULL is specified, the response is ignored. The + * returned string buffer will be 0 terminated like any ordinary C + * string. + * @param resp_len The length of the supplied response buffer. If + * 0, then any response will be ignored. No more than resp_len + * characters (including the trailing 0 byte) will be returned. + * @return The number of bytes read, or -1 on error. + */ + int uartat_command_with_response(const uartat_context dev, + const char *cmd, char *resp, + size_t resp_len); + + /** + * Send an AT command and ignore any response. This is a + * shorthand version of uartat_command_with_response(), and is + * equivalent to calling uartat_command_with_response(dev, cmd, + * NULL, 0). + * + * @param dev Device context + * @param cmd The AT command to send, including the "AT" prefix + * and a terminating carriage return ("\r"). + */ + void uartat_command(const uartat_context dev, const char *cmd); + + /** + * Read characters for up to millis milliseconds, returning + * as soon as the wait_string is found. + * + * @param dev Device context + * @param cmd The command to send + * @param resp The response character buffer + * @param resp_len The maximum size of the response buffer + * @param wait_string The string to search for + * @param millis The maximum number of milliseconds to look for + * the wait_string. + * @return true if the wait_string was found in the response, + * false otherwise. + */ + bool uartat_command_waitfor(const uartat_context dev, const char *cmd, + char *resp, size_t resp_len, + const char *wait_string, + unsigned int millis); + + /** + * Set a flow control method for the UART. By default, during + * initialization, flow control is disabled. + * + * @param dev Device context + * @param fc One of the UARTAT_FLOW_CONTROL_T values. + * @return the UPM result. + */ + upm_result_t uartat_set_flow_control(const uartat_context dev, + UARTAT_FLOW_CONTROL_T fc); + + /** + * Look for a string in a buffer. This is a utility function that + * can be used to indicate if a given string is present in a + * supplied buffer. The search is case sensitive. + * + * @param dev Device context + * @param buffer The 0 teminated buffer in which to search. + * @param str The 0 teminated string to search for. + * @return true if the string was found, false otherwise. + */ + bool uartat_find(const uartat_context dev, const char *buffer, + const char *str); + + /** + * Filter out carriage returns (CR) from response buffers if + * enabled. This operates only on the response buffers returned + * from uartat_command_with_response(), uartat_command(), and + * uartat_command_waitfor(). + * + * @param dev Device context + * @param enable true to filter out CR's, false otherwise + */ + void uartat_filter_cr(const uartat_context dev, bool enable); + +#ifdef __cplusplus +} +#endif diff --git a/src/uartat/uartat.hpp b/src/uartat/uartat.hpp new file mode 100644 index 00000000..66c210c8 --- /dev/null +++ b/src/uartat/uartat.hpp @@ -0,0 +1,260 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2017 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#pragma once + +#include +#include + +#include "uartat.h" + +namespace upm { + /** + * @brief API for a generic AT command based UART device + * @defgroup uartat libupm-uartat + * @ingroup uart + */ + + /** + * @library uartat + * @sensor uartat + * @comname Generic AT command based UART device + * @type other + * @con uart + * + * @brief API for a Generic AT command based UART device + * + * This is a generic UART device driver for accessing UART based + * devices that utilize an "AT" command set. Typically these + * devices are Radios, Modems, and similar devices that are + * configured and controlled by emitting "AT" commands. + */ + + class UARTAT { + public: + + /** + * UARTAT object constructor for a UART specified by MRAA number. + * + * @param uart Specify which uart to use. + * @param baudrate Specify the baudrate to use. + */ + UARTAT(unsigned int uart, unsigned int baudrate); + + /** + * UARTAT object constructor for a UART specified by PATH (ex: + * /dev/ttyUSB0) + * + * @param uart_path Specify path of UART device. + * @param baudrate Specify the baudrate to use. + */ + UARTAT(std::string uart_path, unsigned int baudrate); + + /** + * UARTAT object destructor + */ + ~UARTAT(); + + /** + * Read character data from the device. + * + * @param size The maximum number of characters to read. + * @return string containing the data read. + */ + std::string readStr(size_t size); + + /** + * Write character data to the device. + * + * @param buffer The string containing the data to write. + * @return The number of bytes written. + */ + int writeStr(std::string buffer); + + /** + * Set the baudrate of the device. + * + * @param baudrate The baud rate to set for the device. + */ + void setBaudrate(unsigned int baudrate); + + /** + * Set the default time, in milliseconds, to wait for data to + * arrive after sending a command. + * + * @param wait_ms The response delay to set, in milliseconds. + */ + void setResponseWaitTime(unsigned int wait_time); + + /** + * Determine whether there is data available to be read. In the + * case of a UART, this function will wait up to "millis" + * milliseconds for data to become available. In the case of an I2C + * device, the millis argument is ignored and the function will + * return immediately, indicating whether data is available. + * + * @param millis The number of milliseconds to wait for data to + * become available. + * @return true if data is available to be read, false otherwise. + */ + bool dataAvailable(unsigned int millis); + + /** + * Place the device in AT command mode. Many devices operate in a + * transparent mode and an AT command mode. Command mode is + * required to issue AT based commands. When in transparent mode, + * the device will usually listen for a special sequence of + * characters and delays, indicating that AT command mode should + * be entered. + * + * On most devices, the sequence is: + * +++ + * + * For most devices, the wait time is 1 second (1000 ms) and the + * character sequence is "+++". These options can often be + * configured on the device. + * + * This function will wait millis milliseconds, write the command + * characters (typically "+++"), then wait millis milliseconds again. + * At this time a read will be attempted, looking for the "OK" + * response indicating command mode was successfully entered. + * + * @param cmd_chars The character sequence to write, typically "+++". + * @param guard_ms The number of milliseconds to delay before and + * after the cmd_chars are written. + * @return true if AT command mode ("OK" detected) was + * successfully entered, false otherwise. + */ + bool commandMode(const std::string cmd_chars, unsigned int guard_ms); + + /** + * Check to see if the device is in command mode. This is + * accomplished by sending an "AT\r" command and seeing if + * "OK" or "0" is returned. + * + * @return true if AT command mode was detected, false otherwise. + */ + bool inCommandMode(); + + /** + * Read and throw away any data currently available to be + * read. This is useful to avoid reading data that might have + * been the result of a previous command interfering with data + * you currently want to read. This function is automatically + * called by commandWithResponse(), command(), and + * commandWaitfor() prior to writing the requested command to + * the device. + * + */ + void drain(); + + /** + * Send an AT command and optionally return a response. + * + * @param cmd A character string containing the AT command to + * send, including the "AT" prefix and a terminating carriage + * return ("\r"). + * @param resp_len The maximum number of characters to read from the + * device. + * @return The device response string, if any. + */ + std::string commandWithResponse(const std::string cmd, size_t resp_len); + + /** + * Send an AT command and return a response, while waiting for + * a specific string. If the string isn't found the returned + * string will be empty. If the string is found, the function + * will return immediately. + * + * @param cmd A character string containing the AT command to + * send, including the "AT" prefix and a terminating carriage + * return ("\r"). + * @param resp_len The maximum number of characters to read from the + * device. + * @param wait_string The string to look for. If found, the + * response will be returned immediately regardless of the + * timeout setting. + * @param millis The maximum number of milliseconds to wait + * for the string. + * @return A string containing the response if the search + * string was found, otherwise and empty string is returned. + */ + std::string commandWaitFor(const std::string cmd, size_t resp_len, + const std::string waitString, + unsigned int millis); + + /** + * Send an AT command and ignore any response. + * + * @param cmd The AT command to send, including the "AT" prefix + * and a terminating carriage return ("\r"). + */ + void command(const std::string cmd); + + /** + * This is a convenience method that converts each CR (\r) in a + * string to a LF (\n) and returns it. This is useful for + * outputting the response to an AT command for instance, which is + * often CR terminated. + * + * @param str The string to convert + * @return The converted string + */ + std::string stringCR2LF(std::string str); + + /** + * Set a flow control method for the UART. By default, during + * initialization, flow control is disabled. + * + * @param fc One of the UARTAT_FLOW_CONTROL_T values. + */ + void setFlowControl(UARTAT_FLOW_CONTROL_T fc); + + /** + * Look for a string in a buffer. This is a utility function that + * can be used to indicate if a given string is present in a + * supplied buffer. The search is case sensitive. + * + * @param buffer The string buffer in which to search. + * @param str The string to search for. + * @return true if the string was found, false otherwise. + */ + bool find(const std::string buffer, const std::string str); + + /** + * Filter out carriage returns (CR) from response buffers if + * enabled. This operates only on the response buffers returned + * from commandWithResponse(), command(), and + * commandWaitfor(). + * + * @param enable true to filter out CR's, false otherwise + */ + void filterCR(bool enable); + + protected: + // uartat device context + uartat_context m_uartat; + + private: + }; +} diff --git a/src/uartat/uartat_defs.h b/src/uartat/uartat_defs.h new file mode 100644 index 00000000..1d3932fd --- /dev/null +++ b/src/uartat/uartat_defs.h @@ -0,0 +1,54 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2017 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + + // possible flow control methods + typedef enum { + UARTAT_FLOW_CONTROL_NONE = 0, + UARTAT_FLOW_CONTROL_HARD, // hardware flow control + UARTAT_FLOW_CONTROL_SOFT // software flow control + } UARTAT_FLOW_CONTROL_T; + + + // Numeric response codes via ITU-T V25Ter recommendations + typedef enum { + UARTAT_RESPONSE_CODE_OK = 0, + UARTAT_RESPONSE_CODE_CONNECT = 1, + UARTAT_RESPONSE_CODE_RING = 2, + UARTAT_RESPONSE_CODE_NO_CARRIER = 3, + UARTAT_RESPONSE_CODE_ERROR = 4, + // 5 unassigned + UARTAT_RESPONSE_CODE_NO_DIALTONE = 6, + UARTAT_RESPONSE_CODE_BUSY = 7, + UARTAT_RESPONSE_CODE_NO_ANSWER = 8 + } UARTAT_RESPONSE_CODE_T; + +#ifdef __cplusplus +} +#endif