Skip to content

Commit

Permalink
Moving AtClient to a separate library. (#2497)
Browse files Browse the repository at this point in the history
* Initial refactoring.
* Add sample.
* Add documentation.
  • Loading branch information
slaff authored Apr 1, 2022
1 parent ee78364 commit 7c7298f
Show file tree
Hide file tree
Showing 11 changed files with 190 additions and 76 deletions.
41 changes: 41 additions & 0 deletions Sming/Libraries/AtClient/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
At Client
=========

.. highlight:: c++

Introduction
------------

AT commands were initially instructions used to control a modem. AT is the abbreviation of ATtention.
Every command line starts with "AT" or "at".
If interested a good background article is `Hayes Command Set <https://en.wikipedia.org/wiki/Hayes_command_set>`__.

Nowadays also other devices allow communication via AT commands as for example GSM/GPRS modems, GPS trackers, web cameras and more.

This library simplifies the communication with such devices.

Usage
-----

1. Add ``COMPONENT_DEPENDS += AtClient`` to your application componenent.mk file.
2. Add these lines to your application::

#include <AtClient.h>
namespace
{
AtClient* atClient;
// ...
} // namespace
void init()
{
Serial.begin(SERIAL_BAUD_RATE);
Serial.systemDebugOutput(true);

atClient = new AtClient(Serial);
atClient->send("ATE0\r");
// ...
}
2 changes: 2 additions & 0 deletions Sming/Libraries/AtClient/component.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
COMPONENT_SRCDIRS := src
COMPONENT_INCDIRS := $(COMPONENT_SRCDIRS)
9 changes: 9 additions & 0 deletions Sming/Libraries/AtClient/samples/Webcam/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#####################################################################
#### Please don't change this file. Use component.mk instead ####
#####################################################################

ifndef SMING_HOME
$(error SMING_HOME is not set: please configure it as an environment variable)
endif

include $(SMING_HOME)/project.mk
7 changes: 7 additions & 0 deletions Sming/Libraries/AtClient/samples/Webcam/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Webcam
======

Sample demonstrating communication with a web camera via AT commands using :library:`AtClient` library.
The complete set of commands for the sample web camera are in the docs/ directory.

More about the web camera module and commands can be found `here <https://raymondtunning.wordpress.com/2016/09/08/a20-the-new-gprsgsm-product/>`__.
Empty file.
56 changes: 56 additions & 0 deletions Sming/Libraries/AtClient/samples/Webcam/app/application.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#include <SmingCore.h>
#include <AtClient.h>

AtClient* atClient;

namespace
{
int pictureSize;

bool processSize(AtClient& atClient, String& reply)
{
/*
* ================
AT+CAMCAP capture picture
Set Command:
Capture and store a Picture local and returns bytes of captured picture
AT+CAMCAP
+CAMCAP:<bytes>
OK
on error:
+CME ERROR:
* ================
*/
if(reply.indexOf("+CME ERROR") == 0) {
debug_e("Unable to capture image");
return false;
}

// Read the size of the captured image
String length = reply.substring(8);
pictureSize = atoi(length.c_str());

debug_d("Picture size: %d", pictureSize);

return true;
}

} // namespace

void init()
{
Serial.begin(SERIAL_BAUD_RATE);
Serial.systemDebugOutput(true);
Serial.swap(); //swap to GPIO13 and GPIO15

atClient = new AtClient(Serial);

// The commands below will be queued and executed
// in the same order as defined below.
// One command needs to finish successfully in order for the next one to start.
atClient->send("ATE0\r");
atClient->send("AT+CAMSTOP\r");

atClient->send("AT+CAMSTART=1\r");
atClient->send("AT+CAMCAP\r", processSize);
}
3 changes: 3 additions & 0 deletions Sming/Libraries/AtClient/samples/Webcam/component.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## If project doesn't require networking, saves RAM and build time
DISABLE_NETWORK := 1
COMPONENT_DEPENDS := AtClient
Binary file not shown.
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,13 @@
****/

#include "AtClient.h"
#include "Clock.h"

#ifndef debugf
#define debugf(fmt, ...)
#endif
#include <debug_progmem.h>
#include <Clock.h>

AtClient::AtClient(HardwareSerial* stream) : stream(stream)
AtClient::AtClient(HardwareSerial& stream) : stream(stream)
{
this->stream->onDataReceived(StreamDataReceivedDelegate(&AtClient::processor, this));
stream.onDataReceived(StreamDataReceivedDelegate(&AtClient::processor, this));
}

void AtClient::processor(Stream& source, char arrivedChar, uint16_t availableCharsCount)
Expand All @@ -29,7 +27,7 @@ void AtClient::processor(Stream& source, char arrivedChar, uint16_t availableCha
return;
}

if(state == eAtError) {
if(state == State::Error) {
// discard input at error state
return;
}
Expand All @@ -46,17 +44,17 @@ void AtClient::processor(Stream& source, char arrivedChar, uint16_t availableCha
}

commandTimer.stop();
debugf("Processing: %d ms, %s", millis(), currentCommand.text.substring(0, 20).c_str());
debug_d("Processing: %d ms, %s", millis(), currentCommand.text.substring(0, 20).c_str());

char response[availableCharsCount];
for(int i = 0; i < availableCharsCount; i++) {
response[i] = stream->read();
response[i] = stream.read();
if(response[i] == '\r' || response[i] == '\n') {
response[i] = '\0';
}
}

debugf("Got response: %s", response);
debug_d("Got response: %s", response);

String reply(response);
if(reply.indexOf(AT_REPLY_OK) + reply.indexOf(currentCommand.response2) == -2) {
Expand All @@ -67,7 +65,7 @@ void AtClient::processor(Stream& source, char arrivedChar, uint16_t availableCha
}

if(currentCommand.breakOnError) {
state = eAtError;
state = State::Error;
return;
}
}
Expand All @@ -83,7 +81,7 @@ void AtClient::processor(Stream& source, char arrivedChar, uint16_t availableCha

void AtClient::send(const String& text, const String& altResponse, uint32_t timeoutMs, unsigned retries)
{
AtCommand atCommand;
Command atCommand;
atCommand.text = text;
atCommand.response2 = altResponse;
atCommand.timeout = timeoutMs;
Expand All @@ -92,9 +90,9 @@ void AtClient::send(const String& text, const String& altResponse, uint32_t time
send(atCommand);
}

void AtClient::send(const String& text, AtReceiveCallback onReceive, uint32_t timeoutMs, unsigned retries)
void AtClient::send(const String& text, ReceiveCallback onReceive, uint32_t timeoutMs, unsigned retries)
{
AtCommand atCommand;
Command atCommand;
atCommand.text = text;
atCommand.onReceive = onReceive;
atCommand.timeout = timeoutMs;
Expand All @@ -103,9 +101,9 @@ void AtClient::send(const String& text, AtReceiveCallback onReceive, uint32_t ti
send(atCommand);
}

void AtClient::send(const String& text, AtCompleteCallback onComplete, uint32_t timeoutMs, unsigned retries)
void AtClient::send(const String& text, CompleteCallback onComplete, uint32_t timeoutMs, unsigned retries)
{
AtCommand atCommand;
Command atCommand;
atCommand.text = text;
atCommand.onComplete = onComplete;
atCommand.timeout = timeoutMs;
Expand All @@ -116,7 +114,7 @@ void AtClient::send(const String& text, AtCompleteCallback onComplete, uint32_t

// Low Level Communication Functions

void AtClient::send(AtCommand command)
void AtClient::send(Command command)
{
if(currentCommand.text.length()) {
queue.enqueue(command);
Expand All @@ -126,21 +124,21 @@ void AtClient::send(AtCommand command)
sendDirect(command);
}

void AtClient::sendDirect(AtCommand command)
void AtClient::sendDirect(Command command)
{
state = eAtRunning;
state = State::Running;
commandTimer.stop();
currentCommand = command;
stream->print(command.text);
debugf("Sent: timeout: %d, current %d ms, name: %s", currentCommand.timeout, millis(),
command.text.substring(0, 20).c_str());
stream.print(command.text);
debug_d("Sent: timeout: %d, current %d ms, name: %s", currentCommand.timeout, millis(),
command.text.substring(0, 20).c_str());
commandTimer.initializeMs(currentCommand.timeout, TimerDelegate(&AtClient::ticker, this)).startOnce();
}

// Low Level Queue Functions
void AtClient::resend()
{
state = eAtOK;
state = State::OK;
if(currentCommand.text.length()) {
sendDirect(currentCommand);
return;
Expand All @@ -151,12 +149,12 @@ void AtClient::resend()

void AtClient::next()
{
if(state == eAtError) {
debugf("We are at error state! No next");
if(state == State::Error) {
debug_e("We are at error state! No next");
return;
}

state = eAtOK;
state = State::OK;
currentCommand.text = "";
if(queue.count() > 0) {
send(queue.dequeue());
Expand All @@ -165,22 +163,22 @@ void AtClient::next()

void AtClient::ticker()
{
debugf("Ticker =================> ");
debug_d("Ticker =================> ");
if(!currentCommand.text.length()) {
commandTimer.stop();
debugf("Error: Timeout without command?!");
debug_e("Error: Timeout without command?!");
return;
}

currentCommand.retries--;
debugf("Retries: %d", currentCommand.retries);
debug_d("Retries: %d", currentCommand.retries);
if(currentCommand.retries > 0) {
commandTimer.restart();
sendDirect(currentCommand);
return;
}

state = eAtError;
state = State::Error;

debugf("Timeout: %d ms, %s", millis(), currentCommand.text.c_str());
debug_d("Timeout: %d ms, %s", millis(), currentCommand.text.c_str());
}
Loading

0 comments on commit 7c7298f

Please sign in to comment.