When trying to write to a TCP socket, AT command will trigger binary syntax mode meaning it sends socker_id + number of bytes and than <CR> which is fine.
But then the lib should wait until character @ is received. And this is where the problem is - current implementation doesn't wait and sends it straight after AT command resulting in -3012 error because library is waiting for a response from mdm, but mdm is waiting for that binary data.
[00081375ms][INFO][CELL]: AT TX ( 9): AT+USOWR=
[00081381ms][INFO][CELL]: AT TX ( 1): 0
[00081385ms][INFO][CELL]: AT TX ( 1): ,
[00081390ms][INFO][CELL]: AT TX ( 1): 4
[00081395ms][INFO][CELL]: AT TX ( 1): <cr>
[00081400ms][INFO][CELL]: AT TX ( 5): TEST<00>
[00081406ms][INFO][CELL]: AT RX (13): AT+USOWR=0,4<cr>
[00081424ms][INFO][CELL]: AT RX ( 3): <cr><ln>@
[00141376ms][DBG ][CELL]: AT error -3012
[00141381ms][ERR ][CELL]: Socket 0 sendto 52.215.34.155 error -3012
SARA-G350
GCC-ARM 8.3.1
3d038e55ee4828e8912277044c860ad96c5cbaa6
mbed-cli 1.10.1
#include "mbed.h"
#include "UBLOX_AT.h"
#include "CellularContext.h"
#if MBED_CONF_MBED_TRACE_ENABLE
#include "CellularLog.h"
#endif
EventQueue eQueue(32 * EVENTS_EVENT_SIZE);
CellularContext *mdm;
CellularDevice *mdm_device;
#define MDM_HW_FLOW
class myUblox : public UBLOX_AT {
public:
explicit myUblox(FileHandle *fh);
virtual nsapi_error_t hard_power_on();
virtual nsapi_error_t hard_power_off();
virtual nsapi_error_t soft_power_on();
virtual nsapi_error_t soft_power_off();
private:
DigitalOut pwrOn;
DigitalOut rst;
};
myUblox::myUblox(FileHandle *fh) :
UBLOX_AT(fh),
pwrOn(MDM_PWRON_pin, 0),
rst(MDM_RST_pin, 1) {
}
nsapi_error_t myUblox::hard_power_on() {
rst.write(0);
ThisThread::sleep_for(1000);
return NSAPI_ERROR_OK;
}
nsapi_error_t myUblox::hard_power_off() {
rst.write(1);
return NSAPI_ERROR_OK;
}
nsapi_error_t myUblox::soft_power_on() {
pwrOn.write(1);
ThisThread::sleep_for(150);
pwrOn.write(0);
ThisThread::sleep_for(100);
return NSAPI_ERROR_OK;
}
nsapi_error_t myUblox::soft_power_off() {
pwrOn.write(1);
ThisThread::sleep_for(1000);
pwrOn.write(0);
return NSAPI_ERROR_OK;
}
CellularDevice *CellularDevice::get_target_default_instance() {
static BufferedSerial serial(MDM_TX_pin, MDM_RX_pin, 115200);
#if defined(MDM_HW_FLOW)
serial.set_flow_control(SerialBase::RTSCTS, MDM_RTS_pin, MDM_CTS_pin);
#else
static DigitalOut rts(MDM_RTS_pin, 0);
#endif
static myUblox device(&serial);
return &device;
}
bool serverConnect() {
debug("Server connect\n");
nsapi_error_t ret = NSAPI_ERROR_PARAMETER;
TCPSocket socket;
const char *echo_string = "TEST";
char recv_buf[4];
SocketAddress server;
ret = mdm->gethostbyname("echo.mbedcloudtesting.com", &server);
if (ret == NSAPI_ERROR_OK) {
server.set_port(7);
ret = socket.open(mdm);
if (ret == NSAPI_ERROR_OK) {
debug("open OK\n");
ret = socket.connect(server);
if (ret == NSAPI_ERROR_OK || ret == NSAPI_ERROR_IS_CONNECTED) {
debug("connected\n");
ret = socket.send((void*) echo_string, strlen(echo_string));
if (ret >= NSAPI_ERROR_OK) {
debug("TCP: Sent %i Bytes\n", ret);
nsapi_size_or_error_t n = socket.recv((void*) recv_buf, sizeof(recv_buf));
socket.close();
if (n > 0) {
debug("Received from echo server %i bytes\n", n);
return true;
} else {
debug("Socket recv FAILED: %i\n", n);
}
} else {
debug("Socket send FAILED: %i\n", ret);
}
} else {
debug("connect FAILED: %i\n", ret);
}
} else {
debug("open FAILED\n");
}
} else {
printf("Couldn't resolve remote host, code: %d\n", ret);
}
return false;
}
void mdmConnect() {
debug("MDM connect\n");
nsapi_error_t ret = mdm->connect();
if (ret == NSAPI_ERROR_OK) {
debug("Network connected\n");
serverConnect();
return;
}
debug("Connecting failed\n");
}
bool mdmSetup() {
debug("Device setup\n");
mdm = CellularContext::get_default_instance();
if (mdm != NULL) {
mdm_device = mdm->get_device();
if (mdm_device != NULL) {
mdm_device->hard_power_on();
uint16_t timeout[8] = {1, 2, 4, 8, 16, 32, 64, 128};
mdm_device->set_retry_timeout_array(timeout, 8);
mdm->set_credentials("internet");
// mdm->attach(mdmCb);
mdm->set_blocking(true);
mdmConnect();
return true;
} else {
debug("No interface\n");
}
} else {
debug("No device\n");
}
return false;
}
#if MBED_CONF_MBED_TRACE_ENABLE
static Mutex trace_mutex;
static char time_st[50];
static char* trace_time(size_t ss) {
snprintf(time_st, 49, "[%08llums]", Kernel::get_ms_count());
return time_st;
}
static void trace_wait() {
trace_mutex.lock();
}
static void trace_release() {
trace_mutex.unlock();
}
void trace_init() {
mbed_trace_init();
mbed_trace_prefix_function_set(&trace_time);
mbed_trace_mutex_wait_function_set(trace_wait);
mbed_trace_mutex_release_function_set(trace_release);
mbed_cellular_trace::mutex_wait_function_set(trace_wait);
mbed_cellular_trace::mutex_release_function_set(trace_release);
}
#endif
int main() {
#if MBED_CONF_MBED_TRACE_ENABLE
trace_init();
#endif
Thread eQueueThread;
if (eQueueThread.start(callback(&eQueue, &EventQueue::dispatch_forever)) != osOK) {
debug("eQueueThread error\n");
}
mdmSetup();
mdm->disconnect();
while (1) {
ThisThread::sleep_for(osWaitForever);
}
}
There is an architectural bug in the Mbed AT driver where it assumes that all responses from the modem will be terminated with an end-of-line character like '\n'. This assumption is false for the Ublox AT interface, meaning the Mbed AT framework will need a small but fundamental change to properly support Ublox modems. I don't know how such a disastrous bug got past any kind of QA testing. I've never seen the Mbed Ublox driver work at all. Fortunately, the driver posted by Ublox in the contributed code section (https://os.mbed.com/teams/ublox/) works fine (once you get the APN etc. sorted out).
Internal Jira reference: https://jira.arm.com/browse/MBOTRIAGE-2553
cc @ARMmbed/team-ublox @ARMmbed/cellular-team Please review
Need to read @ and had to wait for 50ms before sending data. Will add fix in ubloxlibraries and generate PR.
PR created #12499.