I want to create simple socket that will communicate with another device via AT commands.
I'm using C++/CLI on Visual 2017.
This is my code
#include "stdafx.h"
#include <conio.h>
using namespace System;
using namespace System::Net;
using namespace System::Net::Sockets;
using namespace System::IO;
int main(array<System::String ^> ^args)
{
int bufferSize = 1024;
array<Byte>^ sendBuffer = gcnew array<Byte>(bufferSize);
array<Byte>^ recvBuffer = gcnew array<Byte>(bufferSize);
try {
// Establish the remote endpoint for the socket.
IPHostEntry^ ipHostInfo = Dns::Resolve("192.168.1.1");
IPAddress^ ipAddress = ipHostInfo->AddressList[0];
IPEndPoint^ remoteEP = gcnew IPEndPoint(ipAddress, 1234);
// Create a TCP/IP socket.
Socket^ socket = gcnew Socket(AddressFamily::InterNetwork,SocketType::Stream, ProtocolType::Tcp);
// Connect the socket to the remote endpoint. Catch any errors.
try {
socket->Connect(remoteEP);
// Encode the data string into a byte array.
array<Byte>^ msg = Text::Encoding::ASCII->GetBytes("AT");
// Send the data through the socket.
int bytesSent = socket->Send(msg);
// Receive the response from the remote device.
int bytesRec = socket->Receive(recvBuffer);
Console::WriteLine("Echoed test = {0}", Text::Encoding::ASCII->GetString(recvBuffer, 0, bytesRec));
// Release the socket.
socket->Shutdown(SocketShutdown::Both);
socket->Close();
}
catch (ArgumentNullException^ ane) {
Console::WriteLine("ArgumentNullException : {0}", ane->ToString());
}
catch (SocketException^ se) {
Console::WriteLine("SocketException : {0}", se->ToString());
}
catch (Exception^ e) {
Console::WriteLine("Unexpected exception : {0}", e->ToString());
}
}
catch (Exception^ e) {
Console::WriteLine(e->ToString());
}
_getch();
return 0;
}
For command there, the response is:
Echoed test = ????????
In ASCII there are weird values: 255,251,3,255,251,1,255,254,1,255,253
The answer should be OK or ERROR
I tested it via Telnet on 192.168.1.1 1234 and it was working fine.
Standard warning: While it's certainly possible to write the main body of your application in C++/CLI, it is not recommended. C++/CLI is intended for interop scenarios: where C# or other .Net code needs to interface with unmanaged C++, C++/CLI can provide the translation between the two. For primary development, it is recommended to use C# if you want managed code, or C++ if you want unmanaged.
That said...
The telnet protocol is not, contrary to popular belief, a raw TCP socket. There is a protocol for communicating options between telnet clients.
What you're seeing there are telnet commands sent from the server. These would be received from by your telnet client, and used to modify how it behaves. This is why everything works when you use a real telnet client: It takes those bytes and interprets the commands properly.
I read the Telnet spec for a few minutes, here's what I was able to decode from the data you posted:
255: IAC, "Interpret as command". This is the escape character for all Telnet commands.
251: WILL: Indicates that the server wants to/is performing an option.
3: SUPPRESS-GO-AHEAD: Apparently Telnet is a half-duplex protocol by default, and the "Go Ahead" is the marker for one side to tell the other, "OK, your turn". This option turns it into a full-duplex connection.
255: IAC
251: WILL
1: ECHO: Indicates that the server will echo back the characters that it receives.
255: IAC
254: DON'T: Indicates that the server is requesting that the client not do something.
1: ECHO: Indicates that the server wants the client to not echo back received characters.
255: IAC
253: DO: Indicates that the server wants the client to turn on some option.
OK, so now that we know what's going on, how do we fix this? I see a few options:
You said you wanted to use "AT Commands" to talk to a device. AT commands are what you use to talk to modems. I'm assuming that you have some serial device, possibly a modem, that you have connected to a little converter device that exposes a serial port as a TCP connection. If so, then there may be some option for that converter device to disable the Telnet protocol, and expose it as "raw", or something similar. If this is true, then that's probably the best option.
You can add code to your program to look for the IAC byte, and handle the bytes that follow it. If it's just a few commands at the beginning of the connection, then you can just expect those fixed bytes; if the commands are sent during the connection, you'll need to handle them everywhere. It's up to you how much you want to handle them. (E.g., if the server says DON'T SUPPRESS-GO-AHEAD, will you send the Go ahead command? Ideally you would, but if your particular connection never says that, then perhaps not.)
There may be a telnet library that will handle the protocol stuff for you. I have not searched for this.
Telnet references:
The main RFC, where the IAC byte and the command bytes are defined: https://www.rfc-editor.org/rfc/rfc854
The ECHO option: https://www.rfc-editor.org/rfc/rfc857
The SUPPRESS-GO-AHEAD option: https://www.rfc-editor.org/rfc/rfc858
Not an official reference, but does list the options that can be specified with WILL, WON'T, DO, and DON'T, and the RFCs they're defined in: http://mars.netanya.ac.il/~unesco/cdrom/booklet/HTML/NETWORKING/node300.html
Related
In the "How to develop an app using the Camera Remote API" toturial it states "The Camera Remote API uses JSON-RPC over HTTP. You can therefore use the Camera Remote APIs with any operating system, such as Android, IOS or Microsoft® Windows®." This stands to reason since the protocols are platform-agnostic. However, in the camera compatibility chart on this page:http://developer.sony.com/develop/cameras/ it states that the Sony Smart Remote Control App must be installed in order to "enable the use of the APIs." Since that app is only iOS and Android, does that mean that the APIs cannot be used on Windows?
I am keenly interested in developing a remote control app for Windows 8 tablets, and then for the Windows 8 phone. But if I cannot control the A5000, A7R, A7, NEX-6, NEX-5R, or NEX-5T, then it becomes far less interesting.
Is it possible to control those cameras with the plain HTTP JSON communication?
Thank you
I don't know if you solved your problem but I have the same issue and I managed to make it work somehow with C++. It took me some time to figure out what I had to do, I have never done any HTTP stuff, even less developed plug and play drivers so I will explain how I did it step by step, as I wish I had been explained.
At the end of the message I have given a link to my entire file, feel free to try it.
I am using boost asio library for every network related issue, and more (everything asynchronous really, this is a great library but very hard to grasp for ignorant people like me...). Most of my functions are partially copy-pasted from the examples in the documentation, this explains why my code is awkward at places. Here is my main function, nothing fancy I instanciate an asio::io_service, create my object (that I wrongly named multicast_manager) and then run the service:
#include <bunch_of_stuff>
using namespace std;
namespace basio = boost::asio;
int main(int argc, char* argv[]) {
try {
basio::io_service io_service;
multicast_manager m(io_service, basio::ip::address::from_string("239.255.255.250"));
io_service.run();
m.parse_description();
m.start_liveview();
io_service.reset();
io_service.run();
m.get_live_image();
io_service.reset();
io_service.run();
} catch (const std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
Discovering the camera over ssdp
First, we have to connect to the camera using its upnp (universal plug and play) feature. The principle is that every upnp device is listening to the multicast port 230.255.255.250:1900 for M-SEARCH request. It means that if you send the proper message to this address, the device will answer by telling you it exists, and give you information to use it. The proper message is given in the documentation. I ran into two pitfalls doing that: first, I omitted to add the newline at the end of my message, as specified in the http standard. So the message you want to send can be build like that:
multicast_manager(basio::io_service& io_service, const basio::ip::address& multicast_address)
: endpoint_(multicast_address, 1900),
socket_(io_service, endpoint_.protocol())
{
stringstream os;
os << "M-SEARCH * HTTP/1.1\r\n";
os << "HOST: 239.255.255.250:1900\r\n";
os << "MAN: \"ssdp:discover\"\r\n";
os << "MX: 4\r\n";
os << "ST: urn:schemas-sony-com:service:ScalarWebAPI:1\r\n";
os << "\r\n";
message_ = os.str();
// ...
The second thing important in this part is to check that the message is sent to the right network interface. In my case, even when it was disabled, it went out through my ethernet card until I changed the right option in the socket, and I solved this issue with the following code:
// ...
socket_.set_option(basio::ip::multicast::outbound_interface(
basio::ip::address_v4::from_string("10.0.1.1")));
socket_.async_send_to(
basio::buffer(message_), endpoint_,
boost::bind(&multicast_manager::handle_send_to, this,
basio::placeholders::error));
}
Now we listen. We listen from where you might ask if you are like me? What port, what address? Well, we don't care: The thing is, when we sent our message, we defined a destination ip and port (in the endpoint constructor). We didn't necessarily define any local address, it is our own ip address (as a matter of fact, we did define it, but only so that it would know which network interface to choose from); and we didn't define any local port, it is in fact chosen automatically (by the OS I guess?). Anyway, the important part is that anyone listening to the multicast group will get our message and know its source, and will respond directly to the correct ip and port. So no need to specify anything here, no need to create a new socket, we just listen to the same socket we sent our message in a bottle:
void handle_send_to(const boost::system::error_code& error)
{
if (!error) {
socket_.async_receive(asio::buffer(data_),
boost::bind(&multicast_manager::handle_read_header, this,
basio::placeholders::error,
basio::placeholders::bytes_transferred));
}
}
If everything goes right, the answer goes along the line of:
HTTP/1.1 200 OK
CACHE-CONTROL: max-age=1800
EXT:
LOCATION: http://10.0.0.1:64321/DmsRmtDesc.xml
SERVER: UPnP/1.0 SonyImagingDevice/1.0
ST: urn:schemas-sony-com:service:ScalarWebAPI:1
USN: uuid:00000000-0005-0010-8000-10a5d09bbeda::urn:schemas-sony-com:service:ScalarWebAPI:1
X-AV-Physical-Unit-Info: pa=""; pl=;
X-AV-Server-Info: av=5.0; hn=""; cn="Sony Corporation"; mn="SonyImagingDevice"; mv="1.0";
To parse this message, I reused the parsing from the boost http client example, except I did it in one go because for some reason I couldn't do an async_read_until with a UDP socket. Anyway, the important part is that the camera received our message; The other important part is the location of the description file DmsRmtDesc.xml.
Retrieving and reading the description file
We need to get DmsRmtDesc.xml. This time we will send a GET request directly to the camera, at the ip address and port specified. This request is something like:
GET /DmsRmtDesc.xml HTTP/1.1
Host: 10.0.0.1
Accept: */*
Connection: close
Don't forget the extra empty line. I don't know what the Connection:close means. The accept line specify the application type of the answer you accept, here we will take any answer. I got the file using the boost http client example, basically I open a socket to 10.0.0.1:64321 and receive the HTPP header which is followed by the content of the file. Now we have a xml file with the address of the webservice we want to use. Let's parse it using boost again, we want to retrieve the camera service address, and maybe the liveview stream address:
namespace bpt = boost::property_tree;
bpt::ptree pt;
bpt::read_xml(content, pt);
liveview_url = pt.get<string>("root.device.av:X_ScalarWebAPI_DeviceInfo.av:X_ScalarWebAPI_ImagingDevice.av:X_ScalarWebAPI_LiveView_URL");
for (bpt::ptree::value_type &v : pt.get_child("root.device.av:X_ScalarWebAPI_DeviceInfo.av:X_ScalarWebAPI_ServiceList")) {
string service = v.second.get<string>("av:X_ScalarWebAPI_ServiceType");
if (service == "camera")
camera_service_url = v.second.get<string>("av:X_ScalarWebAPI_ActionList_URL");
}
Once this is done, we can start sending actual commands to the camera, and using the API.
Sending a command to the camera
The idea is quite simple, we build our command using the json format provided in the documentation, and we send it with a POST http request to the camera service. We will launch the liveview mode, so we send out POST request (we will eventually have to use boost property_tree to build our json string, here I did it manually):
POST /sony/camera HTTP/1.1
Accept: application/json-rpc
Content-Length: 70
Content-Type: application/json-rpc
Host:http://10.0.0.1:10000/sony
{"method": "startLiveview","params" : [],"id" : 1,"version" : "1.0"}
We send it to 10.0.0.1:10000 and wait for the answer:
HTTP/1.1 200 OK
Connection: close
Content-Length: 119
Content-Type: application/json
{"id":1,"result":["http://10.0.0.1:60152/liveview.JPG?%211234%21http%2dget%3a%2a%3aimage%2fjpeg%3a%2a%21%21%21%21%21"]}
We get the liveview url a second time, I don't know which one is better, they are identical...
Anyway, now we know how to send a command to the camera and retrieve its answer, we still have to fetch the image stream.
Fetching an image from the liveview stream
We have the liveview url, and we have the specification in the API reference guide. First thing first, we ask the camera to send us the stream, so we send a GET request to 10.0.0.1:60152:
GET /liveview.JPG?%211234%21http%2dget%3a%2a%3aimage%2fjpeg%3a%2a%21%21%21%21%21 HTTP/1.1
Accept: image/jpeg
Host: 10.0.0.1
And we wait for the answer, that should not take long. The answer begins with the usual HTTTP header:
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Pragma: no-cache
CACHE-CONTROL: no-cache
Content-Type: image/jpeg
transferMode.dlna.org: Interactive
Connection: Keep-Alive
Date: Wed, 09 Jul 2014 14:13:13 GMT
Server: UPnP/1.0 SonyImagingDevice/1.0
According to the documentation, this should be directly followed by the liveview data stream wich consists in theory in:
8 bytes of common header specifying if we are indeed in liveview mode.
128 bytes of payload data giving the size of the jpg data.
n bytes of jpeg data.
And then we get the common header again, indefinitely until we close the socket.
In my case, the common header started with "88\r\n" so I had to discard it, and the jpg data was followed by 10 extra bytes before switching to the next frame, so I had to take that into account. I also had to detect automatically the start of the jpg image because the jpg data started with a text containing a number whose signification I ignore. Most probably these error are due to something I did wrong, or something I don't understand about the technologies I use here.
My code works right now but the last bits are very ad hoc and it definitely need some better checking.
It also needs much refactoring to be usable, but it shows how each step works I guess...
Here is the entire file if you want to try it out.
And here is a working VS project on github.
Thank you for your inquiry.
In the A5000, A7R, A7, NEX-6, NEX-5T, NEX-5R cameras, install the below app.
https://www.playmemoriescameraapps.com/portal/usbdetail.php?eid=IS9104-NPIA09014_00-F00002
This app is to be installed IN the camera and started.
Now you can use "Camera Remote API" to control the above camera from any OS.
I'm having trouble with udp broadcast transactions under boost::asio, related to the following code snippet. Since I'm trying to broadcast in this instance, so deviceIP = "255.255.255.255". devicePort is a specified management port for my device. I want to use an ephemeral local port, so I would prefer if at all possible not to have to socket.bind() after the connection, and the code supports this for unicast by setting localPort = 0.
boost::asio::ip::address_v4 targetIP = boost::asio::ip::address_v4::from_string(deviceIP);
m_targetEndPoint = boost::asio::ip::udp::endpoint(targetIP, devicePort);
m_ioServicePtr = boost::shared_ptr<boost::asio::io_service>(new boost::asio::io_service);
m_socketPtr = boost::shared_ptr<boost::asio::ip::udp::socket>(new boost::asio::ip::udp::socket(*m_ioServicePtr));
m_socketPtr->open(m_targetEndPoint.protocol());
m_socketPtr->set_option(boost::asio::socket_base::broadcast(true));
// If no local port is specified, default parameter is 0
// If local port is specified, bind to that port.
if(localPort != 0)
{
boost::asio::ip::udp::endpoint localEndpoint(boost::asio::ip::address_v4::any(), localPort);
m_socketPtr->bind(localEndpoint);
}
if(m_forceConnect)
m_socketPtr->connect(m_targetEndPoint);
this->AsyncReceive(); // Register Asynch Recieve callback and buffer
m_socketThread = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&MyNetworkBase::RunSocketThread, this))); // Start thread running io_service process
No matter what I do in terms of the following settings, the transmit is working fine, and I can use Wireshark to see the response packets coming back from the device as expected. These response packets are also broadcasts, as the device may be on a different subnet to the pc searching for it.
The issues are extremely strange to my mind, but are as follows:
If I specify the local port and set m_forceConnect=false, everything works fine, and my recieve callback fires appropriately.
If I set m_forceConnect = true in the constructor, but pass in a local port of 0, the transmit works fine, but my receive callback never fires. I would assume this is because the 'target' (m_targetEndpoint) is 255.255.255.255, and since the device has a real IP, the response packet gets filtered out.
(what I actually want) If m_forceConnect = false (and data is transmitted using a send_to call), and local port = 0, therefore taking an ephemeral port, my RX callback immediately fires with an error code 10022, which I believe is an "Invalid Argument" socket error.
Can anyone suggest why I can't use the connection in this manner (not explicitly bound and not explicitly connected)? I obviously don't want to use socket.connect() in this case, as I want to respond to anything I receive. I also don't want to use a predefined port, as I want the user to be able to construct multiple copies of this object without port conflicts.
As some people may have noticed, the overall aim of this is to use the same network-interface base-class to handle both the unicast and broadcast cases. Obviously for the unicast version, I can perfectly happily m_socket->connect() as I know the device's IP, and I receive the responses since they're from the connected IP address, therefore I set m_forceConnect = true, and it all just works.
As all my transmits use send_to, I have also tried to socket.connect(endpoint(ip::addressv4::any(), devicePort), but I get a 'The requested address is not valid in its context' exception when I try it.
I've tried a pretty serious hack:
boost::asio::ip::udp::endpoint localEndpoint(boost::asio::ip::address_v4::any(), m_socketPtr->local_endpoint().port());
m_socketPtr->bind(localEndpoint);
where I extract the initial ephemeral port number and attempt to bind to it, but funnily enough that throws an Invalid Argument exception when I try and bind.
OK, I found a solution to this issue. Under linux it's not necessary, but under windows I discovered that if you are neither binding nor connecting, you must have transmitted something before you make the call to asynch_recieve_from(), the call to which is included within my this->asynch_receive() method.
My solution, make a dummy transmission of an empty string immediately before making the asynch_receive call under windows, so the modified code becomes:
m_socketPtr->set_option(boost::asio::socket_base::broadcast(true));
// If no local port is specified, default parameter is 0
// If local port is specified, bind to that port.
if(localPort != 0)
{
boost::asio::ip::udp::endpoint localEndpoint(boost::asio::ip::address_v4::any(), localPort);
m_socketPtr->bind(localEndpoint);
}
if(m_forceConnect)
m_socketPtr->connect(m_targetEndPoint);
// A dummy TX is required for the socket to acquire the local port properly under windoze
// Transmitting an empty string works fine for this, but the TX must take place BEFORE the first call to Asynch_receive_from(...)
#ifdef WIN32
m_socketPtr->send_to(boost::asio::buffer("", 0), m_targetEndPoint);
#endif
this->AsyncReceive(); // Register Asynch Recieve callback and buffer
m_socketThread = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&MyNetworkBase::RunSocketThread, this)));
It's a bit of a hack in my book, but it is a lot better than implementing all the requirements to defer the call to the asynch recieve until after the first transmission.
I`m using zero mq 3.2.0 C++ libary. I use zmq_connect to connect a port before zmq_bild. But this function return success. How can I know connect fail? My code is:
void *ctx = zmq_ctx_new(1);
void *skt = zmq_socket(ctx, ZMQ_SUB);
int ret = zmq_connect(skt, "tcp://192.168.9.97:5561"); // 192.168.9.97:5561 is not binded
// zmq_connect return zero
This is actually a feature of zeromq, connection status and so on is abstracted away from you. There is no exposed information you can check to see if you're connected or not AFAIK. This means that you can connect even if the server is temporarily down, and zeromq will handle everything when the server comes available later. This can be both a blessing and a curse.
What most people end up doing if they need to know connection status is to implement some sort of heartbeat. REQ/REP ping/pong for example.
Have a look at the lazy pirate pattern for an example of how to ensure reliability from a client perspective.
I am using Arduino to control an SM5100B GSM device, everything works except when I want to send an SMS after receiving another. I get this,
Error code:
O K > + C M G S : 2 5 O K + C M E E R R O R : 4
My code for handling the aforementioned received SMS:
#include <SoftwareSerial.h> //Include the NewSoftSerial library to send serial commands to the cellular module.
char inchar; //Will hold the incoming character from the Serial Port.
SoftwareSerial cell(2,3);
char mobilenumber[] = "0597010129";
void setup() {
//GSM
Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
Serial.println("Initialize GSM module serial port for communication.");
cell.begin(9600);
delay(35000); // give time for GSM module to register on network etc.
Serial.println("delay off");
cell.println("AT+CMGF=1"); // set SMS mode to text
delay(200);
cell.println("AT+CNMI=3,3,0,0"); // set module to send SMS data to serial out upon receipt
delay(200);
}
void loop() {
if(cell.available() >0)//If a character comes in, from the cellular module
{
inchar=cell.read();
Serial.println(inchar);
if (inchar=='#'){ // OK - the start of our command
delay(10);
inchar=cell.read();
Serial.println(inchar);
if (inchar=='a'){
delay(10);
Serial.println("The folowing SMS : \n");
inchar=cell.read();
Serial.println(inchar);
if (inchar=='0'){ //sequance = #a0
Serial.println("#a0 was received");
}
else if (inchar=='1'){//sequance = #a1
Serial.println("#a1 was received ");
sendSms();
}
}
cell.println("AT+CMGD=1,4");// AT command to delete all msgs
Serial.println(" delete all SMS");
}
}//end of if(cell.available() >0) {...}
}
void sendSms(){
//cell.println("AT+CMGF=1"); // set SMS mode to text
cell.print("AT+CMGS="); // now send message...
cell.print((char)34); // ASCII equivalent of "
cell.print(mobilenumber);
cell.println((char)34); // ASCII equivalent of "
delay(500); // give the module some thinking time
cell.print(":D hello m3alleg :D"); // our message to send
cell.println((char)26); // ASCII equivalent of Ctrl-Z
delay(20000);
}
General note about your handling of AT commands.
No, no, no! This way of doing it will never work reliably. You MUST
wait for the > character to be received before sending "text
to send". Or actually it is not just the > character, it is four
characters. Quote from 3GPP specification 27.005:
the TA shall send a four character sequence
<CR><LF><greater_than><space> (IRA 13, 10, 62, 32) after command line
is terminated with <CR>; after that text can be entered from TE to
ME/TA.
(TA (terminal adapter) here means modem and TE (terminal equipment) the sender of AT commands)
For any abortable AT command (and 27.005 clearly states for AT+CMGS
This command should be abortable.) the sending of any character will
abort the operation of the command. To quote ITU V.250:
5.6.1 Aborting commands
...
Aborting
of commands is accomplished by the
transmission from the DTE to the DCE
of any character.
(DCE (data communication equipment) here means modem and DTE (data terminal equipment) the sender of AT commands)
This means that when you send "text to send" before "\r\n> " is sent
by the modem the command will be aborted. There is no way to wait "long
enough" for expecting the response be send. You MUST read and parse
the response text you get back from the modem.
The same applies for the final result code after each command (e.g. OK,
ERROR, CME ERROR and a few more). For instance sending "AT+CMGF=1"
and then sending the next command without first waiting for OK is begging
for problems. So always when sending AT commands, you MUST wait
for the final result code before sending the next command.
Please never, never use delay to wait for any AT command response. It's
as useful as kicking dogs that stand in your way in order to get them
to move. Yes it might actually work some times, but at some point you
will be sorry for taking that approach...
Answer to your question.
Based on the response you get, I can see that your problem is not command
abortion (although your parsing have serious problems as described above
that you should fix), and the CME ERROR is your best clue. From section
"9.2.1 General errors" in 27.007 it gives operation not supported as
description for value 4.
27.005 states that:
If sending fails in a network or an ME error, final result code +CMS ERROR: is returned.
Notice that this is +CMS ERROR and not +CME ERROR, but it is applicable, see below.
I guess that sequence of actions is as following:
The AT command handling part of the SM100B GSM modem accepts the sms data
and formats it in an appropriate format and sends it of to the part of the
modem that communicates with the GSM network. It successfully send the
sms data to the network and reports this back to the AT command handling
part which then prints +CMGS: 25 and final result code OK. However
after a short time the network sends back a rejection message for the sms,
which is then given as the +CME ERROR response.
If my guess above is correct, should the response have been delivered
as +CMS ERROR instead? No, because the final response
has for the AT+CMGS command has already been given (OK), and
returning multiple final result codes for a command should never be done
(except by mistake (note 1)).
And while +CME ERROR can replace the ERROR final result code,
it is not only a final result code. From the AT+CMEE command description:
Set command disables or enables the use of result code +CME ERROR: as an indication of an error relating to
the functionality of the MT. When enabled, MT related errors cause +CME ERROR: final result code instead
of the regular ERROR final result code. ERROR is returned normally when error is related to syntax, invalid parameters,
or TA functionality.
Thus +CME ERROR can both be an final result code as well as an unsolicited
result code (possibly also an intermediate result code).
But could not the AT+CMGS command have waited to receive the network
rejection and returned +CMS ERROR? Probably not. Without knowing too
much about the network details of sms sending, it might be the case
that rejection today might occur at a much later time than before. Such
changes are sometimes a problem with GSM related AT commands which have
an old heritage that was originally tightly tied to GSM behaviour which
some times becomes less and less true as the technology moves to GPRS,
UMTS, LTE, etc.
Note 1:
One of my former colleagues used to complain about the way the standard
have specified voice call handling, because after a ATD1234; command
you first get the final result code OK, and then later when the call is
ended you get a new final result code NO CARRIER. This just horribly
bad design, the call end indication should have been a specific unsolicited
response and not a final response.
So to summarise
Your sms seems to be rejected by the network. Try to find out why.
You also have some serious problems with your AT command handling
that you should fix; there is no way to handle AT commands without
reading and parsing the response text from the modem.
cell.println("AT+CNMI=3,3,0,0"); // set module to send SMS data to
serial out upon receipt
For anyone who is looking for answer to the same problem I had:
I was trying to wake up gsm module from sleep mode by sending sms and it didn't work right away. Phone call goes straight to UART, but for sms you have to use this command to set module to send SMS data to serial port upon receipt .
AT+CNMI=3,3,0,0
Is there anyway that will allow to get all data sent via tcp before the session gets closed?
What I am getting is I have to close the session from server only after that data is received, and moreover I have to manually pass EOL or carriage-return or "\n" or "\r".
Any help and suggestions is appreciated.
You could try setting the SO_LINGER socket option:
GCDAsyncSocket asyncSocket = ...;
struct linger linger;
linger.l_onoff = 1;
linger.l_linger = 30;
int rv = setsockopt([asyncSocket socketFD], SOL_SOCKET, SO_LINGER, &linger, sizeof(linger));
if (rv < 0)
{
// handle error
}
Is there anyway that will allow to get all data sent via tcp before the session gets closed?
You keep reading data from the socket till EOF is received.
What I am getting is I have to close the session from server only after that data is received
Technically, one can close your write side of the TCP connection, using shutdown(socket, SHUT_WR) indicating that this peer is not going to send any more data. But it will still need to read all the data till EOF is seen and then close(socket).
and moreover I have to manually pass EOL or carriage-return or "\n" or "\r"
This has little to do with sockets or TCP, rather with a higher application protocol, such as HTTP. There are libraries available for this popular protocol that simplify this task.