* initial commit
This commit is contained in:
commit
573b67a58c
94
examples/nbIOT_test/nbIOT_test.ino
Normal file
94
examples/nbIOT_test/nbIOT_test.ino
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2015-2016 Sodaq. All rights reserved.
|
||||||
|
|
||||||
|
This file is part of Sodaq_nbIOT.
|
||||||
|
|
||||||
|
Sodaq_nbIOT is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of
|
||||||
|
the License, or(at your option) any later version.
|
||||||
|
|
||||||
|
Sodaq_nbIOT is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with Sodaq_nbIOT. If not, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Sodaq_nbIOT.h"
|
||||||
|
#include <Sodaq_wdt.h>
|
||||||
|
|
||||||
|
#ifdef ARDUINO_SODAQ_EXPLORER
|
||||||
|
#define MODEM_ON_OFF_PIN 7
|
||||||
|
#define MODEM_STREAM Serial
|
||||||
|
#else
|
||||||
|
#error "You need to declare the modem on/off pin and stream for your particular board!"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define DEBUG_STREAM SerialUSB
|
||||||
|
#define DEBUG_STREAM_BAUD 115200
|
||||||
|
|
||||||
|
#define STARTUP_DELAY 5000
|
||||||
|
|
||||||
|
const char* apn = "oceanconnect.t-mobile.nl";
|
||||||
|
const char* cdp = "172.16.14.22";
|
||||||
|
const char* forceOperator = "20416"; // optional - depends on SIM / network
|
||||||
|
|
||||||
|
Sodaq_nbIOT nbiot;
|
||||||
|
|
||||||
|
void showMessageCountFromModem();
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
sodaq_wdt_safe_delay(STARTUP_DELAY);
|
||||||
|
|
||||||
|
DEBUG_STREAM.begin(DEBUG_STREAM_BAUD);
|
||||||
|
MODEM_STREAM.begin(nbiot.getDefaultBaudrate());
|
||||||
|
|
||||||
|
DEBUG_STREAM.print("Initializing and connecting... ");
|
||||||
|
|
||||||
|
nbiot.init(MODEM_STREAM, MODEM_ON_OFF_PIN);
|
||||||
|
nbiot.setDiag(DEBUG_STREAM);
|
||||||
|
|
||||||
|
if (nbiot.connect(apn, cdp, forceOperator)) {
|
||||||
|
DEBUG_STREAM.println("Connected succesfully!");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DEBUG_STREAM.println("Failed to connect!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showMessageCountFromModem();
|
||||||
|
|
||||||
|
const char* message = "Hello World!";
|
||||||
|
DEBUG_STREAM.print("Sending message: \"");
|
||||||
|
DEBUG_STREAM.print(message);
|
||||||
|
DEBUG_STREAM.print("\"... ");
|
||||||
|
|
||||||
|
if (!nbiot.sendMessage(message)) {
|
||||||
|
DEBUG_STREAM.println("Could not queue message!");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DEBUG_STREAM.println("Message queued for transmission!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
if (nbiot.isConnected()) {
|
||||||
|
showMessageCountFromModem();
|
||||||
|
}
|
||||||
|
|
||||||
|
sodaq_wdt_safe_delay(5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void showMessageCountFromModem()
|
||||||
|
{
|
||||||
|
DEBUG_STREAM.print("Pending Messages: ");
|
||||||
|
DEBUG_STREAM.print(nbiot.getSentMessagesCount(Sodaq_nbIOT::Pending));
|
||||||
|
DEBUG_STREAM.print(" | Failed Messages: ");
|
||||||
|
DEBUG_STREAM.println(nbiot.getSentMessagesCount(Sodaq_nbIOT::Error));
|
||||||
|
}
|
||||||
9
library.properties
Normal file
9
library.properties
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
name=Sodaq_nbIOT_SOCK
|
||||||
|
version=1.0.1
|
||||||
|
author=ETH
|
||||||
|
maintainer=
|
||||||
|
sentence=
|
||||||
|
paragraph=
|
||||||
|
category=Device Control
|
||||||
|
url=
|
||||||
|
architectures=avr,samd
|
||||||
4
src/.idea/misc.xml
generated
Normal file
4
src/.idea/misc.xml
generated
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||||
|
</project>
|
||||||
8
src/.idea/modules.xml
generated
Normal file
8
src/.idea/modules.xml
generated
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/src.iml" filepath="$PROJECT_DIR$/.idea/src.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
2
src/.idea/src.iml
generated
Normal file
2
src/.idea/src.iml
generated
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module classpath="CMake" type="CPP_MODULE" version="4" />
|
||||||
355
src/.idea/workspace.xml
generated
Normal file
355
src/.idea/workspace.xml
generated
Normal file
@ -0,0 +1,355 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CMakeRunConfigurationManager" shouldGenerate="true" shouldDeleteObsolete="true" buildAllGenerated="true">
|
||||||
|
<generated>
|
||||||
|
<config projectName="src" targetName="src" />
|
||||||
|
</generated>
|
||||||
|
</component>
|
||||||
|
<component name="CMakeSettings">
|
||||||
|
<configurations>
|
||||||
|
<configuration CONFIG_NAME="Debug" />
|
||||||
|
</configurations>
|
||||||
|
</component>
|
||||||
|
<component name="ChangeListManager">
|
||||||
|
<list default="true" id="a010a727-85aa-4e2e-9247-82a42deb71d3" name="Default" comment="" />
|
||||||
|
<ignored path="$PROJECT_DIR$/cmake-build-debug/" />
|
||||||
|
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
|
||||||
|
<option name="TRACKING_ENABLED" value="true" />
|
||||||
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
|
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||||
|
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||||
|
</component>
|
||||||
|
<component name="FileEditorManager">
|
||||||
|
<leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
|
||||||
|
<file leaf-file-name="Sodaq_nbIOT.h" pinned="false" current-in-tab="false">
|
||||||
|
<entry file="file://$PROJECT_DIR$/Sodaq_nbIOT.h">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="439">
|
||||||
|
<caret line="97" column="18" lean-forward="false" selection-start-line="97" selection-start-column="18" selection-end-line="97" selection-end-column="18" />
|
||||||
|
<folding>
|
||||||
|
<element signature="e#823#843#0" expanded="true" />
|
||||||
|
</folding>
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
</file>
|
||||||
|
<file leaf-file-name="Sodaq_nbIOT.cpp" pinned="false" current-in-tab="true">
|
||||||
|
<entry file="file://$PROJECT_DIR$/Sodaq_nbIOT.cpp">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="568">
|
||||||
|
<caret line="551" column="0" lean-forward="false" selection-start-line="551" selection-start-column="0" selection-end-line="551" selection-end-column="0" />
|
||||||
|
<folding />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
</file>
|
||||||
|
<file leaf-file-name="Sodaq_AT_Device.cpp" pinned="false" current-in-tab="false">
|
||||||
|
<entry file="file://$PROJECT_DIR$/Sodaq_AT_Device.cpp">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="694">
|
||||||
|
<caret line="65" column="22" lean-forward="false" selection-start-line="65" selection-start-column="22" selection-end-line="65" selection-end-column="22" />
|
||||||
|
<folding>
|
||||||
|
<element signature="e#1368#1456#0" expanded="true" />
|
||||||
|
</folding>
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
</file>
|
||||||
|
</leaf>
|
||||||
|
</component>
|
||||||
|
<component name="FindInProjectRecents">
|
||||||
|
<findStrings>
|
||||||
|
<find>readResponse</find>
|
||||||
|
<find>::readResponse</find>
|
||||||
|
<find>wdt</find>
|
||||||
|
<find>println</find>
|
||||||
|
</findStrings>
|
||||||
|
<replaceStrings>
|
||||||
|
<replace />
|
||||||
|
</replaceStrings>
|
||||||
|
</component>
|
||||||
|
<component name="IdeDocumentHistory">
|
||||||
|
<option name="CHANGED_PATHS">
|
||||||
|
<list>
|
||||||
|
<option value="$PROJECT_DIR$/CMakeLists.txt" />
|
||||||
|
<option value="$PROJECT_DIR$/Sodaq_nbIOT.h" />
|
||||||
|
<option value="$PROJECT_DIR$/Sodaq_AT_Device.cpp" />
|
||||||
|
<option value="$PROJECT_DIR$/Sodaq_nbIOT.cpp" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="JsBuildToolGruntFileManager" detection-done="true" sorting="DEFINITION_ORDER" />
|
||||||
|
<component name="JsBuildToolPackageJson" detection-done="true" sorting="DEFINITION_ORDER" />
|
||||||
|
<component name="JsGulpfileManager">
|
||||||
|
<detection-done>true</detection-done>
|
||||||
|
<sorting>DEFINITION_ORDER</sorting>
|
||||||
|
</component>
|
||||||
|
<component name="OCFindUsagesOptions" text="true" ivars="false" properties="true" derivedClasses="false" />
|
||||||
|
<component name="ProjectFrameBounds" extendedState="6">
|
||||||
|
<option name="x" value="339" />
|
||||||
|
<option name="y" value="345" />
|
||||||
|
<option name="width" value="712" />
|
||||||
|
<option name="height" value="1256" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectView">
|
||||||
|
<navigator currentView="ProjectPane" proportions="" version="1">
|
||||||
|
<flattenPackages />
|
||||||
|
<showMembers />
|
||||||
|
<showModules />
|
||||||
|
<showLibraryContents />
|
||||||
|
<hideEmptyPackages />
|
||||||
|
<abbreviatePackageNames />
|
||||||
|
<autoscrollToSource />
|
||||||
|
<autoscrollFromSource />
|
||||||
|
<sortByType />
|
||||||
|
<manualOrder />
|
||||||
|
<foldersAlwaysOnTop value="true" />
|
||||||
|
</navigator>
|
||||||
|
<panes>
|
||||||
|
<pane id="ProjectPane">
|
||||||
|
<subPane>
|
||||||
|
<expand>
|
||||||
|
<path>
|
||||||
|
<item name="src" type="dad4c3:CidrFilesViewHelper$MyProjectTreeStructure$1" />
|
||||||
|
<item name="src" type="462c0819:PsiDirectoryNode" />
|
||||||
|
</path>
|
||||||
|
</expand>
|
||||||
|
<select />
|
||||||
|
</subPane>
|
||||||
|
</pane>
|
||||||
|
</panes>
|
||||||
|
</component>
|
||||||
|
<component name="PropertiesComponent">
|
||||||
|
<property name="settings.editor.selected.configurable" value="SwiftSettings" />
|
||||||
|
<property name="WebServerToolWindowFactoryState" value="false" />
|
||||||
|
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
|
||||||
|
</component>
|
||||||
|
<component name="RunDashboard">
|
||||||
|
<option name="ruleStates">
|
||||||
|
<list>
|
||||||
|
<RuleState>
|
||||||
|
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
|
||||||
|
</RuleState>
|
||||||
|
<RuleState>
|
||||||
|
<option name="name" value="StatusDashboardGroupingRule" />
|
||||||
|
</RuleState>
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="RunManager" selected="Application.src">
|
||||||
|
<configuration name="Build All" type="CMakeRunConfiguration" factoryName="Application" PASS_PARENT_ENVS_2="true" CONFIG_NAME="Debug" EXPLICIT_BUILD_TARGET_NAME="all">
|
||||||
|
<envs />
|
||||||
|
</configuration>
|
||||||
|
<configuration name="src" type="CMakeRunConfiguration" factoryName="Application" PASS_PARENT_ENVS_2="true" PROJECT_NAME="src" TARGET_NAME="src" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="src" RUN_TARGET_NAME="src">
|
||||||
|
<envs />
|
||||||
|
</configuration>
|
||||||
|
<list size="2">
|
||||||
|
<item index="0" class="java.lang.String" itemvalue="Application.Build All" />
|
||||||
|
<item index="1" class="java.lang.String" itemvalue="Application.src" />
|
||||||
|
</list>
|
||||||
|
</component>
|
||||||
|
<component name="ShelveChangesManager" show_recycled="false">
|
||||||
|
<option name="remove_strategy" value="false" />
|
||||||
|
</component>
|
||||||
|
<component name="TaskManager">
|
||||||
|
<task active="true" id="Default" summary="Default task">
|
||||||
|
<changelist id="a010a727-85aa-4e2e-9247-82a42deb71d3" name="Default" comment="" />
|
||||||
|
<created>1507878788635</created>
|
||||||
|
<option name="number" value="Default" />
|
||||||
|
<option name="presentableId" value="Default" />
|
||||||
|
<updated>1507878788635</updated>
|
||||||
|
<workItem from="1507878790372" duration="20313000" />
|
||||||
|
<workItem from="1507977491531" duration="5758000" />
|
||||||
|
<workItem from="1508251701999" duration="6603000" />
|
||||||
|
</task>
|
||||||
|
<servers />
|
||||||
|
</component>
|
||||||
|
<component name="TimeTrackingManager">
|
||||||
|
<option name="totallyTimeSpent" value="32674000" />
|
||||||
|
</component>
|
||||||
|
<component name="ToolWindowManager">
|
||||||
|
<frame x="339" y="345" width="1080" height="1890" extended-state="6" />
|
||||||
|
<editor active="true" />
|
||||||
|
<layout>
|
||||||
|
<window_info id="Project" active="false" anchor="left" auto_hide="true" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.24854933" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
|
||||||
|
<window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
|
||||||
|
<window_info id="CMake" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.23666288" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
|
||||||
|
<window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="7" side_tool="true" content_ui="tabs" />
|
||||||
|
<window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
|
||||||
|
<window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
|
||||||
|
<window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
|
||||||
|
<window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
|
||||||
|
<window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="2" side_tool="true" content_ui="tabs" />
|
||||||
|
<window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
|
||||||
|
<window_info id="Data View" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
|
||||||
|
<window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
|
||||||
|
<window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
|
||||||
|
<window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
|
||||||
|
<window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
|
||||||
|
<window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="combo" />
|
||||||
|
<window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
|
||||||
|
<window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
|
||||||
|
</layout>
|
||||||
|
</component>
|
||||||
|
<component name="TypeScriptGeneratedFilesManager">
|
||||||
|
<option name="version" value="1" />
|
||||||
|
</component>
|
||||||
|
<component name="VcsContentAnnotationSettings">
|
||||||
|
<option name="myLimit" value="2678400000" />
|
||||||
|
</component>
|
||||||
|
<component name="XDebuggerManager">
|
||||||
|
<breakpoint-manager />
|
||||||
|
<watches-manager />
|
||||||
|
</component>
|
||||||
|
<component name="editorHistoryManager">
|
||||||
|
<entry file="file://$PROJECT_DIR$/CMakeLists.txt">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="391">
|
||||||
|
<caret line="23" column="0" lean-forward="false" selection-start-line="23" selection-start-column="0" selection-end-line="23" selection-end-column="0" />
|
||||||
|
<folding />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/Sodaq_nbIOT.h">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="1377">
|
||||||
|
<caret line="81" column="0" lean-forward="true" selection-start-line="81" selection-start-column="0" selection-end-line="81" selection-end-column="0" />
|
||||||
|
<folding>
|
||||||
|
<element signature="e#823#843#0" expanded="true" />
|
||||||
|
</folding>
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/Sodaq_AT_Device.cpp">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="408">
|
||||||
|
<caret line="24" column="10" lean-forward="true" selection-start-line="24" selection-start-column="10" selection-end-line="24" selection-end-column="10" />
|
||||||
|
<folding />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/Sodaq_nbIOT.cpp">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="8211">
|
||||||
|
<caret line="483" column="8" lean-forward="false" selection-start-line="483" selection-start-column="8" selection-end-line="483" selection-end-column="8" />
|
||||||
|
<folding />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/CMakeLists.txt">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="0">
|
||||||
|
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
|
||||||
|
<folding />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/Sodaq_nbIOT.h">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="1275">
|
||||||
|
<caret line="75" column="14" lean-forward="false" selection-start-line="75" selection-start-column="14" selection-end-line="75" selection-end-column="14" />
|
||||||
|
<folding>
|
||||||
|
<element signature="e#823#843#0" expanded="true" />
|
||||||
|
</folding>
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/Sodaq_nbIOT.cpp">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="374">
|
||||||
|
<caret line="22" column="0" lean-forward="true" selection-start-line="22" selection-start-column="0" selection-end-line="22" selection-end-column="0" />
|
||||||
|
<folding />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/Sodaq_AT_Device.cpp">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="5100">
|
||||||
|
<caret line="300" column="33" lean-forward="true" selection-start-line="300" selection-start-column="33" selection-end-line="300" selection-end-column="33" />
|
||||||
|
<folding />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/../../Sodaq_wdt/src/Sodaq_wdt.cpp">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="629">
|
||||||
|
<caret line="133" column="5" lean-forward="false" selection-start-line="133" selection-start-column="5" selection-end-line="133" selection-end-column="5" />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file:///usr/lib/gcc/x86_64-linux-gnu/5/include/stddef.h">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="578">
|
||||||
|
<caret line="215" column="22" lean-forward="false" selection-start-line="215" selection-start-column="22" selection-end-line="215" selection-end-column="22" />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/Sodaq_OnOffBee.h">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="0">
|
||||||
|
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/Sodaq_AT_Device.h">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="1428">
|
||||||
|
<caret line="87" column="51" lean-forward="false" selection-start-line="87" selection-start-column="51" selection-end-line="87" selection-end-column="51" />
|
||||||
|
<folding />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/../../Sodaq_wdt/src/Sodaq_wdt.h">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="0">
|
||||||
|
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/CMakeLists.txt">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="255">
|
||||||
|
<caret line="15" column="0" lean-forward="true" selection-start-line="15" selection-start-column="0" selection-end-line="15" selection-end-column="0" />
|
||||||
|
<folding />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file:///usr/include/stdio.h">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="384">
|
||||||
|
<caret line="432" column="11" lean-forward="false" selection-start-line="432" selection-start-column="11" selection-end-line="432" selection-end-column="11" />
|
||||||
|
<folding />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/Sodaq_nbIOT.h">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="439">
|
||||||
|
<caret line="97" column="18" lean-forward="false" selection-start-line="97" selection-start-column="18" selection-end-line="97" selection-end-column="18" />
|
||||||
|
<folding>
|
||||||
|
<element signature="e#823#843#0" expanded="true" />
|
||||||
|
</folding>
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/Sodaq_AT_Device.cpp">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="694">
|
||||||
|
<caret line="65" column="22" lean-forward="false" selection-start-line="65" selection-start-column="22" selection-end-line="65" selection-end-column="22" />
|
||||||
|
<folding>
|
||||||
|
<element signature="e#1368#1456#0" expanded="true" />
|
||||||
|
</folding>
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
<entry file="file://$PROJECT_DIR$/Sodaq_nbIOT.cpp">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="568">
|
||||||
|
<caret line="551" column="0" lean-forward="false" selection-start-line="551" selection-start-column="0" selection-end-line="551" selection-end-column="0" />
|
||||||
|
<folding />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
23
src/CMakeLists.txt
Normal file
23
src/CMakeLists.txt
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.8)
|
||||||
|
project(src)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
|
|
||||||
|
set(ARDUINO_BASE_LIBDIR /opt/arduino-1.8.5)
|
||||||
|
set(ARDUINO_SAMD_DIR $ENV{HOME}/.arduino15/packages/arduino/hardware/samd/1.6.16)
|
||||||
|
set(ARDUINO_USER_LIBDIR $ENV{HOME}/Arduino/libraries)
|
||||||
|
|
||||||
|
set(ARDUINO_INCLUDE_DIRS
|
||||||
|
${ARDUINO_SAMD_DIR}/cores/arduino
|
||||||
|
${ARDUINO_SAMD_DIR}/libraries/SPI
|
||||||
|
${ARDUINO_USER_LIBDIR}/Sodaq_wdt/src
|
||||||
|
${ARDUINO_BASE_LIBDIR}/libraries/Ethernet/src
|
||||||
|
)
|
||||||
|
|
||||||
|
include_directories (${WAKAAMA_SOURCES_DIR} ${SHARED_INCLUDE_DIRS} ${ARDUINO_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
set(SOURCE_FILES
|
||||||
|
Sodaq_AT_Device.cpp
|
||||||
|
Sodaq_nbIOT.cpp)
|
||||||
|
|
||||||
|
add_executable(src ${SOURCE_FILES})
|
||||||
385
src/Sodaq_AT_Device.cpp
Normal file
385
src/Sodaq_AT_Device.cpp
Normal file
@ -0,0 +1,385 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2013-2015 Sodaq. All rights reserved.
|
||||||
|
|
||||||
|
This file is part of Sodaq_nbIOT.
|
||||||
|
|
||||||
|
Sodaq_nbIOT is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of
|
||||||
|
the License, or(at your option) any later version.
|
||||||
|
|
||||||
|
Sodaq_nbIOT is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with Sodaq_nbIOT. If not, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Sodaq_AT_Device.h"
|
||||||
|
|
||||||
|
#define DEBUG
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
#define debugPrintLn(...) { if (!this->_disableDiag && this->_diagStream) this->_diagStream->println(__VA_ARGS__); }
|
||||||
|
#define debugPrint(...) { if (!this->_disableDiag && this->_diagStream) this->_diagStream->print(__VA_ARGS__); }
|
||||||
|
#warning "Debug mode is ON"
|
||||||
|
#else
|
||||||
|
#define debugPrintLn(...)
|
||||||
|
#define debugPrint(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CR "\r"
|
||||||
|
#define LF "\n"
|
||||||
|
#define CRLF "\r\n"
|
||||||
|
|
||||||
|
// TODO this needs to be set in the compiler directives. Find something else to do
|
||||||
|
#define SODAQ_AT_DEVICE_TERMINATOR CRLF
|
||||||
|
|
||||||
|
#ifndef SODAQ_AT_DEVICE_TERMINATOR
|
||||||
|
#warning "SODAQ_AT_DEVICE_TERMINATOR is not set"
|
||||||
|
#define SODAQ_AT_DEVICE_TERMINATOR CRLF
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define SODAQ_AT_DEVICE_TERMINATOR_LEN (sizeof(SODAQ_AT_DEVICE_TERMINATOR) - 1) // without the NULL terminator
|
||||||
|
|
||||||
|
#define SODAQ_AT_DEVICE_DEFAULT_INPUT_BUFFER_SIZE 250
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
Sodaq_AT_Device::Sodaq_AT_Device() :
|
||||||
|
_modemStream(0),
|
||||||
|
_diagStream(0),
|
||||||
|
_disableDiag(false),
|
||||||
|
_inputBufferSize(SODAQ_AT_DEVICE_DEFAULT_INPUT_BUFFER_SIZE),
|
||||||
|
_inputBuffer(0),
|
||||||
|
_onoff(0),
|
||||||
|
_baudRateChangeCallbackPtr(0),
|
||||||
|
_appendCommand(false),
|
||||||
|
_startOn(0)
|
||||||
|
{
|
||||||
|
this->_isBufferInitialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turns the modem on and returns true if successful.
|
||||||
|
bool Sodaq_AT_Device::on()
|
||||||
|
{
|
||||||
|
_startOn = millis();
|
||||||
|
|
||||||
|
if (!isOn()) {
|
||||||
|
if (_onoff) {
|
||||||
|
_onoff->on();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for power up
|
||||||
|
bool timeout = true;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < 10; i++) {
|
||||||
|
if (isAlive()) {
|
||||||
|
timeout = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeout) {
|
||||||
|
debugPrintLn("Error: No Reply from Modem");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isOn(); // this essentially means isOn() && isAlive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turns the modem off and returns true if successful.
|
||||||
|
bool Sodaq_AT_Device::off()
|
||||||
|
{
|
||||||
|
// No matter if it is on or off, turn it off.
|
||||||
|
if (_onoff) {
|
||||||
|
_onoff->off();
|
||||||
|
}
|
||||||
|
|
||||||
|
return !isOn();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the modem is on.
|
||||||
|
bool Sodaq_AT_Device::isOn() const
|
||||||
|
{
|
||||||
|
if (_onoff) {
|
||||||
|
return _onoff->isOn();
|
||||||
|
}
|
||||||
|
|
||||||
|
// No onoff. Let's assume it is on.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sodaq_AT_Device::writeProlog()
|
||||||
|
{
|
||||||
|
if (!_appendCommand) {
|
||||||
|
debugPrint(">> ");
|
||||||
|
_appendCommand = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write a byte, as binary data
|
||||||
|
size_t Sodaq_AT_Device::writeByte(uint8_t value)
|
||||||
|
{
|
||||||
|
return _modemStream->write(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Sodaq_AT_Device::print(const String& buffer)
|
||||||
|
{
|
||||||
|
writeProlog();
|
||||||
|
debugPrint(buffer);
|
||||||
|
|
||||||
|
return _modemStream->print(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Sodaq_AT_Device::print(const char buffer[])
|
||||||
|
{
|
||||||
|
writeProlog();
|
||||||
|
debugPrint(buffer);
|
||||||
|
|
||||||
|
return _modemStream->print(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Sodaq_AT_Device::print(char value)
|
||||||
|
{
|
||||||
|
writeProlog();
|
||||||
|
debugPrint(value);
|
||||||
|
|
||||||
|
return _modemStream->print(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t Sodaq_AT_Device::print(unsigned char value, int base)
|
||||||
|
{
|
||||||
|
writeProlog();
|
||||||
|
debugPrint(value, base);
|
||||||
|
|
||||||
|
return _modemStream->print(value, base);
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t Sodaq_AT_Device::print(int value, int base)
|
||||||
|
{
|
||||||
|
writeProlog();
|
||||||
|
debugPrint(value, base);
|
||||||
|
|
||||||
|
return _modemStream->print(value, base);
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t Sodaq_AT_Device::print(unsigned int value, int base)
|
||||||
|
{
|
||||||
|
writeProlog();
|
||||||
|
debugPrint(value, base);
|
||||||
|
|
||||||
|
return _modemStream->print(value, base);
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t Sodaq_AT_Device::print(long value, int base)
|
||||||
|
{
|
||||||
|
writeProlog();
|
||||||
|
debugPrint(value, base);
|
||||||
|
|
||||||
|
return _modemStream->print(value, base);
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t Sodaq_AT_Device::print(unsigned long value, int base)
|
||||||
|
{
|
||||||
|
writeProlog();
|
||||||
|
debugPrint(value, base);
|
||||||
|
|
||||||
|
return _modemStream->print(value, base);
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t Sodaq_AT_Device::println(const __FlashStringHelper* ifsh)
|
||||||
|
{
|
||||||
|
size_t n = print(ifsh);
|
||||||
|
n += println();
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Sodaq_AT_Device::println(const String& s)
|
||||||
|
{
|
||||||
|
size_t n = print(s);
|
||||||
|
n += println();
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Sodaq_AT_Device::println(const char c[])
|
||||||
|
{
|
||||||
|
size_t n = print(c);
|
||||||
|
n += println();
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Sodaq_AT_Device::println(char c)
|
||||||
|
{
|
||||||
|
size_t n = print(c);
|
||||||
|
n += println();
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Sodaq_AT_Device::println(unsigned char b, int base)
|
||||||
|
{
|
||||||
|
size_t i = print(b, base);
|
||||||
|
return i + println();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Sodaq_AT_Device::println(int num, int base)
|
||||||
|
{
|
||||||
|
size_t i = print(num, base);
|
||||||
|
return i + println();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Sodaq_AT_Device::println(unsigned int num, int base)
|
||||||
|
{
|
||||||
|
size_t i = print(num, base);
|
||||||
|
return i + println();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Sodaq_AT_Device::println(long num, int base)
|
||||||
|
{
|
||||||
|
size_t i = print(num, base);
|
||||||
|
return i + println();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Sodaq_AT_Device::println(unsigned long num, int base)
|
||||||
|
{
|
||||||
|
size_t i = print(num, base);
|
||||||
|
return i + println();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Sodaq_AT_Device::println(double num, int digits)
|
||||||
|
{
|
||||||
|
writeProlog();
|
||||||
|
debugPrint(num, digits);
|
||||||
|
|
||||||
|
return _modemStream->println(num, digits);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Sodaq_AT_Device::println(const Printable& x)
|
||||||
|
{
|
||||||
|
size_t i = print(x);
|
||||||
|
return i + println();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Sodaq_AT_Device::println(void)
|
||||||
|
{
|
||||||
|
debugPrintLn();
|
||||||
|
size_t i = print(SODAQ_AT_DEVICE_TERMINATOR);
|
||||||
|
_appendCommand = false;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initializes the input buffer and makes sure it is only initialized once.
|
||||||
|
// Safe to call multiple times.
|
||||||
|
void Sodaq_AT_Device::initBuffer()
|
||||||
|
{
|
||||||
|
debugPrintLn("[initBuffer]");
|
||||||
|
|
||||||
|
// make sure the buffers are only initialized once
|
||||||
|
if (!_isBufferInitialized) {
|
||||||
|
this->_inputBuffer = static_cast<char*>(malloc(this->_inputBufferSize));
|
||||||
|
|
||||||
|
_isBufferInitialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the modem stream.
|
||||||
|
void Sodaq_AT_Device::setModemStream(Stream& stream)
|
||||||
|
{
|
||||||
|
this->_modemStream = &stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a character from the modem stream if read within _timeout ms or -1 otherwise.
|
||||||
|
int Sodaq_AT_Device::timedRead(uint32_t timeout) const
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
uint32_t _startMillis = millis();
|
||||||
|
|
||||||
|
do {
|
||||||
|
c = _modemStream->read();
|
||||||
|
|
||||||
|
if (c >= 0) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
} while (millis() - _startMillis < timeout);
|
||||||
|
|
||||||
|
return -1; // -1 indicates timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fills the given "buffer" with characters read from the modem stream up to "length"
|
||||||
|
// maximum characters and until the "terminator" character is found or a character read
|
||||||
|
// times out (whichever happens first).
|
||||||
|
// The buffer does not contain the "terminator" character or a null terminator explicitly.
|
||||||
|
// Returns the number of characters written to the buffer, not including null terminator.
|
||||||
|
size_t Sodaq_AT_Device::readBytesUntil(char terminator, char* buffer, size_t length, uint32_t timeout)
|
||||||
|
{
|
||||||
|
if (length < 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t index = 0;
|
||||||
|
|
||||||
|
while (index < length) {
|
||||||
|
int c = timedRead(timeout);
|
||||||
|
|
||||||
|
if (c < 0 || c == terminator) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
*buffer++ = static_cast<char>(c);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index < length) {
|
||||||
|
*buffer = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO distinguise timeout from empty string?
|
||||||
|
// TODO return error for overflow?
|
||||||
|
return index; // return number of characters, not including null terminator
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fills the given "buffer" with up to "length" characters read from the modem stream.
|
||||||
|
// It stops when a character read times out or "length" characters have been read.
|
||||||
|
// Returns the number of characters written to the buffer.
|
||||||
|
size_t Sodaq_AT_Device::readBytes(uint8_t* buffer, size_t length, uint32_t timeout)
|
||||||
|
{
|
||||||
|
size_t count = 0;
|
||||||
|
|
||||||
|
while (count < length) {
|
||||||
|
int c = timedRead(timeout);
|
||||||
|
|
||||||
|
if (c < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
*buffer++ = static_cast<uint8_t>(c);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO distinguise timeout from empty string?
|
||||||
|
// TODO return error for overflow?
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads a line (up to the SODAQ_GSM_TERMINATOR) from the modem stream into the "buffer".
|
||||||
|
// The buffer is terminated with null.
|
||||||
|
// Returns the number of bytes read, not including the null terminator.
|
||||||
|
size_t Sodaq_AT_Device::readLn(char* buffer, size_t size, uint32_t timeout)
|
||||||
|
{
|
||||||
|
// Use size-1 to leave room for a string terminator
|
||||||
|
size_t len = readBytesUntil(SODAQ_AT_DEVICE_TERMINATOR[SODAQ_AT_DEVICE_TERMINATOR_LEN - 1], buffer, size - 1, timeout);
|
||||||
|
|
||||||
|
// check if the terminator is more than 1 characters, then check if the first character of it exists
|
||||||
|
// in the calculated position and terminate the string there
|
||||||
|
if ((SODAQ_AT_DEVICE_TERMINATOR_LEN > 1) && (buffer[len - (SODAQ_AT_DEVICE_TERMINATOR_LEN - 1)] == SODAQ_AT_DEVICE_TERMINATOR[0])) {
|
||||||
|
len -= SODAQ_AT_DEVICE_TERMINATOR_LEN - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// terminate string, there should always be room for it (see size-1 above)
|
||||||
|
buffer[len] = '\0';
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
182
src/Sodaq_AT_Device.h
Normal file
182
src/Sodaq_AT_Device.h
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2015-2016 Sodaq. All rights reserved.
|
||||||
|
|
||||||
|
This file is part of Sodaq_nbIOT.
|
||||||
|
|
||||||
|
Sodaq_nbIOT is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of
|
||||||
|
the License, or(at your option) any later version.
|
||||||
|
|
||||||
|
Sodaq_nbIOT is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with Sodaq_nbIOT. If not, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SODAQ_AT_DEVICE_h
|
||||||
|
#define _SODAQ_AT_DEVICE_h
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <Stream.h>
|
||||||
|
#include "Sodaq_OnOffBee.h"
|
||||||
|
|
||||||
|
// Response type (returned by readResponse() and parser methods).
|
||||||
|
enum ResponseTypes {
|
||||||
|
ResponseNotFound = 0,
|
||||||
|
ResponseOK = 1,
|
||||||
|
ResponseError = 2,
|
||||||
|
ResponsePrompt = 3,
|
||||||
|
ResponseTimeout = 4,
|
||||||
|
ResponseEmpty = 5,
|
||||||
|
ResponsePendingExtra = 6,
|
||||||
|
};
|
||||||
|
|
||||||
|
// IP type
|
||||||
|
typedef uint32_t IP_t;
|
||||||
|
|
||||||
|
// callback for changing the baudrate of the modem stream.
|
||||||
|
typedef void (*BaudRateChangeCallbackPtr)(uint32_t newBaudrate);
|
||||||
|
|
||||||
|
#define SODAQ_AT_DEVICE_DEFAULT_READ_MS 5000 // Used in readResponse()
|
||||||
|
|
||||||
|
class Sodaq_AT_Device
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Constructor
|
||||||
|
Sodaq_AT_Device();
|
||||||
|
virtual ~Sodaq_AT_Device() {}
|
||||||
|
|
||||||
|
// Sets the onoff instance
|
||||||
|
void setOnOff(Sodaq_OnOffBee& onoff) { _onoff = &onoff; }
|
||||||
|
|
||||||
|
// Turns the modem on and returns true if successful.
|
||||||
|
bool on();
|
||||||
|
|
||||||
|
// Turns the modem off and returns true if successful.
|
||||||
|
bool off();
|
||||||
|
|
||||||
|
// Sets the optional "Diagnostics and Debug" stream.
|
||||||
|
void setDiag(Stream& stream) { _diagStream = &stream; }
|
||||||
|
void setDiag(Stream* stream) { _diagStream = stream; }
|
||||||
|
|
||||||
|
// Sets the size of the input buffer.
|
||||||
|
// Needs to be called before init().
|
||||||
|
void setInputBufferSize(size_t value) { this->_inputBufferSize = value; };
|
||||||
|
|
||||||
|
// Returns the default baud rate of the modem.
|
||||||
|
// To be used when initializing the modem stream for the first time.
|
||||||
|
virtual uint32_t getDefaultBaudrate() = 0;
|
||||||
|
|
||||||
|
// Enables the change of the baud rate to a higher speed when the modem is ready to do so.
|
||||||
|
// Needs a callback in the main application to re-initialize the stream.
|
||||||
|
void enableBaudrateChange(BaudRateChangeCallbackPtr callback) { _baudRateChangeCallbackPtr = callback; };
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// The stream that communicates with the device.
|
||||||
|
Stream* _modemStream;
|
||||||
|
|
||||||
|
// The (optional) stream to show debug information.
|
||||||
|
Stream* _diagStream;
|
||||||
|
bool _disableDiag;
|
||||||
|
|
||||||
|
// The size of the input buffer. Equals SODAQ_GSM_MODEM_DEFAULT_INPUT_BUFFER_SIZE
|
||||||
|
// by default or (optionally) a user-defined value when using USE_DYNAMIC_BUFFER.
|
||||||
|
size_t _inputBufferSize;
|
||||||
|
|
||||||
|
// Flag to make sure the buffers are not allocated more than once.
|
||||||
|
bool _isBufferInitialized;
|
||||||
|
|
||||||
|
// The buffer used when reading from the modem. The space is allocated during init() via initBuffer().
|
||||||
|
char* _inputBuffer;
|
||||||
|
|
||||||
|
// The on-off pin power controller object.
|
||||||
|
Sodaq_OnOffBee* _onoff;
|
||||||
|
|
||||||
|
// The callback for requesting baudrate change of the modem stream.
|
||||||
|
BaudRateChangeCallbackPtr _baudRateChangeCallbackPtr;
|
||||||
|
|
||||||
|
// This flag keeps track if the next write is the continuation of the current command
|
||||||
|
// A Carriage Return will reset this flag.
|
||||||
|
bool _appendCommand;
|
||||||
|
|
||||||
|
// Keep track when connect started. Use this to record various status changes.
|
||||||
|
uint32_t _startOn;
|
||||||
|
|
||||||
|
// Initializes the input buffer and makes sure it is only initialized once.
|
||||||
|
// Safe to call multiple times.
|
||||||
|
void initBuffer();
|
||||||
|
|
||||||
|
// Returns true if the modem is ON (and replies to "AT" commands without timing out)
|
||||||
|
virtual bool isAlive() = 0;
|
||||||
|
|
||||||
|
// Returns true if the modem is on.
|
||||||
|
bool isOn() const;
|
||||||
|
|
||||||
|
// Sets the modem stream.
|
||||||
|
void setModemStream(Stream& stream);
|
||||||
|
|
||||||
|
// Returns a character from the modem stream if read within _timeout ms or -1 otherwise.
|
||||||
|
int timedRead(uint32_t timeout = 1000) const;
|
||||||
|
|
||||||
|
// Fills the given "buffer" with characters read from the modem stream up to "length"
|
||||||
|
// maximum characters and until the "terminator" character is found or a character read
|
||||||
|
// times out (whichever happens first).
|
||||||
|
// The buffer does not contain the "terminator" character or a null terminator explicitly.
|
||||||
|
// Returns the number of characters written to the buffer, not including null terminator.
|
||||||
|
size_t readBytesUntil(char terminator, char* buffer, size_t length, uint32_t timeout = 1000);
|
||||||
|
|
||||||
|
// Fills the given "buffer" with up to "length" characters read from the modem stream.
|
||||||
|
// It stops when a character read times out or "length" characters have been read.
|
||||||
|
// Returns the number of characters written to the buffer.
|
||||||
|
size_t readBytes(uint8_t* buffer, size_t length, uint32_t timeout = 1000);
|
||||||
|
|
||||||
|
// Reads a line from the modem stream into the "buffer". The line terminator is not
|
||||||
|
// written into the buffer. The buffer is terminated with null.
|
||||||
|
// Returns the number of bytes read, not including the null terminator.
|
||||||
|
size_t readLn(char* buffer, size_t size, uint32_t timeout = 1000);
|
||||||
|
|
||||||
|
// Reads a line from the modem stream into the input buffer.
|
||||||
|
// Returns the number of bytes read.
|
||||||
|
size_t readLn() { return readLn(_inputBuffer, _inputBufferSize); };
|
||||||
|
|
||||||
|
// Write a byte
|
||||||
|
size_t writeByte(uint8_t value);
|
||||||
|
|
||||||
|
// Write the command prolog (just for debugging
|
||||||
|
void writeProlog();
|
||||||
|
|
||||||
|
size_t print(const __FlashStringHelper*);
|
||||||
|
size_t print(const String&);
|
||||||
|
size_t print(const char[]);
|
||||||
|
size_t print(char);
|
||||||
|
size_t print(unsigned char, int = DEC);
|
||||||
|
size_t print(int, int = DEC);
|
||||||
|
size_t print(unsigned int, int = DEC);
|
||||||
|
size_t print(long, int = DEC);
|
||||||
|
size_t print(unsigned long, int = DEC);
|
||||||
|
size_t print(double, int = 2);
|
||||||
|
size_t print(const Printable&);
|
||||||
|
|
||||||
|
size_t println(const __FlashStringHelper*);
|
||||||
|
size_t println(const String& s);
|
||||||
|
size_t println(const char[]);
|
||||||
|
size_t println(char);
|
||||||
|
size_t println(unsigned char, int = DEC);
|
||||||
|
size_t println(int, int = DEC);
|
||||||
|
size_t println(unsigned int, int = DEC);
|
||||||
|
size_t println(long, int = DEC);
|
||||||
|
size_t println(unsigned long, int = DEC);
|
||||||
|
size_t println(double, int = 2);
|
||||||
|
size_t println(const Printable&);
|
||||||
|
size_t println(void);
|
||||||
|
|
||||||
|
virtual ResponseTypes readResponse(char* buffer, size_t size, size_t* outSize, uint32_t timeout = SODAQ_AT_DEVICE_DEFAULT_READ_MS) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
21
src/Sodaq_OnOffBee.h
Normal file
21
src/Sodaq_OnOffBee.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#ifndef SODAQ_ONOFFBEE_H_
|
||||||
|
#define SODAQ_ONOFFBEE_H_
|
||||||
|
/*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief This class is used to switch on or off a (SODAQ) Bee device.
|
||||||
|
*
|
||||||
|
* It's a pure virtual class, so you'll have to implement a specialized
|
||||||
|
* class.
|
||||||
|
*/
|
||||||
|
class Sodaq_OnOffBee
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~Sodaq_OnOffBee() {}
|
||||||
|
virtual void on() = 0;
|
||||||
|
virtual void off() = 0;
|
||||||
|
virtual bool isOn() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* SODAQ_ONOFFBEE_H_ */
|
||||||
918
src/Sodaq_nbIOT.cpp
Normal file
918
src/Sodaq_nbIOT.cpp
Normal file
@ -0,0 +1,918 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2015-2016 Sodaq. All rights reserved.
|
||||||
|
|
||||||
|
This file is part of Sodaq_nbIOT.
|
||||||
|
|
||||||
|
Sodaq_nbIOT is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of
|
||||||
|
the License, or(at your option) any later version.
|
||||||
|
|
||||||
|
Sodaq_nbIOT is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with Sodaq_nbIOT. If not, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Sodaq_nbIOT.h"
|
||||||
|
#include <Sodaq_wdt.h>
|
||||||
|
|
||||||
|
#define DEBUG
|
||||||
|
#define AT_CMD_SLEEP_TIME 10
|
||||||
|
|
||||||
|
#define STR_AT "AT"
|
||||||
|
#define STR_RESPONSE_OK "OK"
|
||||||
|
#define STR_RESPONSE_ERROR "ERROR"
|
||||||
|
#define STR_RESPONSE_CME_ERROR "+CME ERROR:"
|
||||||
|
#define STR_RESPONSE_CMS_ERROR "+CMS ERROR:"
|
||||||
|
|
||||||
|
#define DEBUG_STR_ERROR "[ERROR]: "
|
||||||
|
|
||||||
|
#define NIBBLE_TO_HEX_CHAR(i) ((i <= 9) ? ('0' + i) : ('A' - 10 + i))
|
||||||
|
#define HIGH_NIBBLE(i) ((i >> 4) & 0x0F)
|
||||||
|
#define LOW_NIBBLE(i) (i & 0x0F)
|
||||||
|
|
||||||
|
#define HEX_CHAR_TO_NIBBLE(c) ((c >= 'A') ? (c - 'A' + 0x0A) : (c - '0'))
|
||||||
|
#define HEX_PAIR_TO_BYTE(h, l) ((HEX_CHAR_TO_NIBBLE(h) << 4) + HEX_CHAR_TO_NIBBLE(l))
|
||||||
|
|
||||||
|
#define STR_HELPER(x) #x
|
||||||
|
#define STR(x) STR_HELPER(x)
|
||||||
|
|
||||||
|
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
#define debugPrintLn(...) { if (!this->_disableDiag && this->_diagStream) this->_diagStream->println(__VA_ARGS__); }
|
||||||
|
#define debugPrint(...) { if (!this->_disableDiag && this->_diagStream) this->_diagStream->print(__VA_ARGS__); }
|
||||||
|
#warning "Debug mode is ON"
|
||||||
|
#else
|
||||||
|
#define debugPrintLn(...)
|
||||||
|
#define debugPrint(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define DEFAULT_CID "1"
|
||||||
|
|
||||||
|
#define NO_IP_ADDRESS ((IP_t)0)
|
||||||
|
|
||||||
|
#define IP_FORMAT "%d.%d.%d.%d"
|
||||||
|
|
||||||
|
#define IP_TO_TUPLE(x) (uint8_t)(((x) >> 24) & 0xFF), \
|
||||||
|
(uint8_t)(((x) >> 16) & 0xFF), \
|
||||||
|
(uint8_t)(((x) >> 8) & 0xFF), \
|
||||||
|
(uint8_t)(((x) >> 0) & 0xFF)
|
||||||
|
|
||||||
|
#define TUPLE_TO_IP(o1, o2, o3, o4) ((((IP_t)o1) << 24) | (((IP_t)o2) << 16) | \
|
||||||
|
(((IP_t)o3) << 8) | (((IP_t)o4) << 0))
|
||||||
|
|
||||||
|
#define SOCKET_FAIL -1
|
||||||
|
|
||||||
|
#define SOCKET_COUNT 7
|
||||||
|
|
||||||
|
#define NOW (uint32_t)millis()
|
||||||
|
|
||||||
|
typedef struct NameValuePair {
|
||||||
|
const char *Name;
|
||||||
|
const char *Value;
|
||||||
|
} NameValuePair;
|
||||||
|
|
||||||
|
const uint8_t nConfigCount = 3;
|
||||||
|
static NameValuePair nConfig[nConfigCount] = {
|
||||||
|
{"AUTOCONNECT", "FALSE"},
|
||||||
|
{"CR_0354_0338_SCRAMBLING", "FALSE"},
|
||||||
|
{"CR_0859_SI_AVOID", "FALSE"}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Sodaq_nbIotOnOff : public Sodaq_OnOffBee {
|
||||||
|
public:
|
||||||
|
Sodaq_nbIotOnOff();
|
||||||
|
|
||||||
|
void init(int onoffPin);
|
||||||
|
|
||||||
|
void on();
|
||||||
|
|
||||||
|
void off();
|
||||||
|
|
||||||
|
bool isOn();
|
||||||
|
|
||||||
|
private:
|
||||||
|
int8_t _onoffPin;
|
||||||
|
bool _onoff_status;
|
||||||
|
};
|
||||||
|
|
||||||
|
static Sodaq_nbIotOnOff sodaq_nbIotOnOff;
|
||||||
|
|
||||||
|
static inline bool is_timedout(uint32_t from, uint32_t nr_ms) __attribute__((always_inline));
|
||||||
|
|
||||||
|
static inline bool is_timedout(uint32_t from, uint32_t nr_ms) {
|
||||||
|
return (millis() - from) > nr_ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sodaq_nbIOT::Sodaq_nbIOT() :
|
||||||
|
_lastRSSI(0),
|
||||||
|
_CSQtime(0),
|
||||||
|
_minRSSI(-113) // dBm
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the modem replies to "AT" commands without timing out.
|
||||||
|
bool Sodaq_nbIOT::isAlive() {
|
||||||
|
// _disableDiag = true;
|
||||||
|
println(STR_AT);
|
||||||
|
// @todo check if necessary
|
||||||
|
delay(150);
|
||||||
|
|
||||||
|
return (readResponse(NULL, 450) == ResponseOK);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initializes the modem instance. Sets the modem stream and the on-off power pins.
|
||||||
|
void Sodaq_nbIOT::init(Stream &stream, int8_t onoffPin) {
|
||||||
|
debugPrintLn("[init] started.");
|
||||||
|
|
||||||
|
initBuffer(); // safe to call multiple times
|
||||||
|
|
||||||
|
setModemStream(stream);
|
||||||
|
|
||||||
|
sodaq_nbIotOnOff.init(onoffPin);
|
||||||
|
_onoff = &sodaq_nbIotOnOff;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sodaq_nbIOT::setRadioActive(bool on) {
|
||||||
|
print("AT+CFUN=");
|
||||||
|
println(on ? "1" : "0");
|
||||||
|
|
||||||
|
return (readResponse() == ResponseOK);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sodaq_nbIOT::setIndicationsActive(bool on) {
|
||||||
|
print("AT+NSMI=");
|
||||||
|
println(on ? "1" : "0");
|
||||||
|
|
||||||
|
if (readResponse() != ResponseOK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
print("AT+NNMI=");
|
||||||
|
println(on ? "1" : "0");
|
||||||
|
|
||||||
|
return (readResponse() == ResponseOK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Read the next response from the modem
|
||||||
|
|
||||||
|
Notice that we're collecting URC's here. And in the process we could
|
||||||
|
be updating:
|
||||||
|
_socketPendingBytes[] if +UUSORD: is seen
|
||||||
|
_socketClosedBit[] if +UUSOCL: is seen
|
||||||
|
*/
|
||||||
|
ResponseTypes Sodaq_nbIOT::readResponse(char *buffer, size_t size,
|
||||||
|
CallbackMethodPtr parserMethod, void *callbackParameter,
|
||||||
|
void *callbackParameter2,
|
||||||
|
size_t *outSize, uint32_t timeout) {
|
||||||
|
ResponseTypes response = ResponseNotFound;
|
||||||
|
uint32_t from = NOW;
|
||||||
|
|
||||||
|
do {
|
||||||
|
// 250ms, how many bytes at which baudrate?
|
||||||
|
int count = readLn(buffer, size, 500);
|
||||||
|
sodaq_wdt_reset();
|
||||||
|
|
||||||
|
if (count > 0) {
|
||||||
|
if (outSize) {
|
||||||
|
*outSize = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_disableDiag && strncmp(buffer, "OK", 2) != 0) {
|
||||||
|
_disableDiag = false;
|
||||||
|
}
|
||||||
|
debugPrint("[rdResp]: ");
|
||||||
|
debugPrintLn(buffer);
|
||||||
|
|
||||||
|
// TODO handle socket URC
|
||||||
|
|
||||||
|
//int param1, param2;
|
||||||
|
//if (sscanf(buffer, "+UUSORD: %d,%d", ¶m1, ¶m2) == 2) {
|
||||||
|
// uint16_t socket_nr = param1;
|
||||||
|
// uint16_t nr_bytes = param2;
|
||||||
|
// debugPrint("Unsolicited: Socket ");
|
||||||
|
// debugPrint(socket_nr);
|
||||||
|
// debugPrint(": ");
|
||||||
|
// debugPrint(param2);
|
||||||
|
// debugPrintLn(" bytes pending");
|
||||||
|
// if (socket_nr < ARRAY_SIZE(_socketPendingBytes)) {
|
||||||
|
// _socketPendingBytes[socket_nr] = nr_bytes;
|
||||||
|
// }
|
||||||
|
// continue;
|
||||||
|
//}
|
||||||
|
//else if (sscanf(buffer, "+UUSOCL: %d", ¶m1) == 1) {
|
||||||
|
// uint16_t socket_nr = param1;
|
||||||
|
// if (socket_nr < ARRAY_SIZE(_socketPendingBytes)) {
|
||||||
|
// debugPrint("Unsolicited: Socket ");
|
||||||
|
// debugPrint(socket_nr);
|
||||||
|
// debugPrint(": ");
|
||||||
|
// debugPrintLn("closed by remote");
|
||||||
|
|
||||||
|
// _socketClosedBit[socket_nr] = true;
|
||||||
|
// if (socket_nr == _openTCPsocket) {
|
||||||
|
// _openTCPsocket = -1;
|
||||||
|
// // Report this other software layers
|
||||||
|
// if (_tcpClosedHandler) {
|
||||||
|
// _tcpClosedHandler();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// continue;
|
||||||
|
//}
|
||||||
|
|
||||||
|
if (startsWith(STR_AT, buffer)) {
|
||||||
|
continue; // skip echoed back command
|
||||||
|
}
|
||||||
|
|
||||||
|
_disableDiag = false;
|
||||||
|
|
||||||
|
if (startsWith(STR_RESPONSE_OK, buffer)) {
|
||||||
|
return ResponseOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startsWith(STR_RESPONSE_ERROR, buffer) ||
|
||||||
|
startsWith(STR_RESPONSE_CME_ERROR, buffer) ||
|
||||||
|
startsWith(STR_RESPONSE_CMS_ERROR, buffer)) {
|
||||||
|
return ResponseError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parserMethod) {
|
||||||
|
ResponseTypes parserResponse = parserMethod(response, buffer, count, callbackParameter,
|
||||||
|
callbackParameter2);
|
||||||
|
|
||||||
|
if ((parserResponse != ResponseEmpty) && (parserResponse != ResponsePendingExtra)) {
|
||||||
|
return parserResponse;
|
||||||
|
} else {
|
||||||
|
// ?
|
||||||
|
// ResponseEmpty indicates that the parser was satisfied
|
||||||
|
// Continue until "OK", "ERROR", or whatever else.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent calling the parser again.
|
||||||
|
// This could happen if the input line is too long. It will be split
|
||||||
|
// and the next readLn will return the next part.
|
||||||
|
// The case of "ResponsePendingExtra" is an exception to this, thus waiting for more replies to be parsed.
|
||||||
|
if (parserResponse != ResponsePendingExtra) {
|
||||||
|
parserMethod = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// at this point, the parserMethod has ran and there is no override response from it,
|
||||||
|
// so if there is some other response recorded, return that
|
||||||
|
// (otherwise continue iterations until timeout)
|
||||||
|
if (response != ResponseNotFound) {
|
||||||
|
debugPrintLn("** response != ResponseNotFound");
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(10); // TODO Why do we need this delay?
|
||||||
|
} while (!is_timedout(from, timeout));
|
||||||
|
|
||||||
|
if (outSize) {
|
||||||
|
*outSize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
debugPrintLn("[rdResp]: timed out");
|
||||||
|
return ResponseTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sodaq_nbIOT::setApn(const char *apn) {
|
||||||
|
print("AT+CGDCONT=" DEFAULT_CID ",\"IP\",\"");
|
||||||
|
print(apn);
|
||||||
|
println("\"");
|
||||||
|
|
||||||
|
return (readResponse() == ResponseOK);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sodaq_nbIOT::setCdp(const char *cdp) {
|
||||||
|
print("AT+NCDP=");
|
||||||
|
println(cdp);
|
||||||
|
|
||||||
|
return (readResponse() == ResponseOK);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turns on and initializes the modem, then connects to the network and activates the data connection.
|
||||||
|
bool Sodaq_nbIOT::connect(const char *apn, const char *cdp, const char *forceOperator) {
|
||||||
|
if (!on()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!setRadioActive(false)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!checkAndApplyNconfig()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
reboot();
|
||||||
|
|
||||||
|
if (!on()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!setApn(apn) || !setCdp(cdp)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO turn on
|
||||||
|
if (!setIndicationsActive(false)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!setRadioActive(true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (forceOperator && forceOperator[0] != '\0') {
|
||||||
|
print("AT+COPS=1,2,\"");
|
||||||
|
print(forceOperator);
|
||||||
|
println("\"");
|
||||||
|
|
||||||
|
if (readResponse() != ResponseOK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!waitForSignalQuality()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!attachGprs()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we got this far we succeeded
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sodaq_nbIOT::reboot() {
|
||||||
|
println("AT+NRB");
|
||||||
|
|
||||||
|
// wait up to 2000ms for the modem to come up
|
||||||
|
uint32_t start = millis();
|
||||||
|
while ((readResponse() != ResponseOK) && !is_timedout(start, 2000)) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sodaq_nbIOT::checkAndApplyNconfig() {
|
||||||
|
bool applyParam[nConfigCount];
|
||||||
|
|
||||||
|
println("AT+NCONFIG?");
|
||||||
|
|
||||||
|
if (readResponse<bool, uint8_t>(_nconfigParser, applyParam, NULL) == ResponseOK) {
|
||||||
|
for (uint8_t i = 0; i < nConfigCount; i++) {
|
||||||
|
debugPrint(nConfig[i].Name);
|
||||||
|
if (!applyParam[i]) {
|
||||||
|
debugPrintLn("... CHANGE");
|
||||||
|
setNconfigParam(nConfig[i].Name, nConfig[i].Value);
|
||||||
|
} else {
|
||||||
|
debugPrintLn("... OK");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sodaq_nbIOT::setNconfigParam(const char *param, const char *value) {
|
||||||
|
print("AT+NCONFIG=");
|
||||||
|
print(param);
|
||||||
|
print(",");
|
||||||
|
println(value);
|
||||||
|
|
||||||
|
return readResponse() == ResponseOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResponseTypes
|
||||||
|
Sodaq_nbIOT::_nconfigParser(ResponseTypes &response, const char *buffer, size_t size, bool *nconfigEqualsArray,
|
||||||
|
uint8_t *dummy) {
|
||||||
|
if (!nconfigEqualsArray) {
|
||||||
|
return ResponseError;
|
||||||
|
}
|
||||||
|
|
||||||
|
char name[32];
|
||||||
|
char value[32];
|
||||||
|
if (sscanf(buffer, "+NCONFIG: %[^,],%[^\r]", name, value) == 2) {
|
||||||
|
for (uint8_t i = 0; i < nConfigCount; i++) {
|
||||||
|
if (strcmp(nConfig[i].Name, name) == 0) {
|
||||||
|
if (strcmp(nConfig[i].Value, value) == 0) {
|
||||||
|
nconfigEqualsArray[i] = true;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResponsePendingExtra;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResponseError;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sodaq_nbIOT::attachGprs(uint32_t timeout) {
|
||||||
|
uint32_t start = millis();
|
||||||
|
uint32_t delay_count = 500;
|
||||||
|
|
||||||
|
while (!is_timedout(start, timeout)) {
|
||||||
|
println("AT+CGATT=1");
|
||||||
|
|
||||||
|
if (readResponse() == ResponseOK) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
sodaq_wdt_safe_delay(delay_count);
|
||||||
|
|
||||||
|
// Next time wait a little longer, but not longer than 5 seconds
|
||||||
|
if (delay_count < 5000) {
|
||||||
|
delay_count += 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns on and initializes the modem, then connects to the network.
|
||||||
|
*
|
||||||
|
* @author edvidan
|
||||||
|
* @param apn
|
||||||
|
* @param forceOperator
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
bool Sodaq_nbIOT::connectSocket() {
|
||||||
|
// if (!on()) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!setRadioActive(false)) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!checkAndApplyNconfig()) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// reboot();
|
||||||
|
//
|
||||||
|
// if (!on()) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// TODO turn on
|
||||||
|
// if (!setIndicationsActive(false)) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!setRadioActive(true)) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (forceOperator && forceOperator[0] != '\0') {
|
||||||
|
// print("AT+COPS=1,2,\"");
|
||||||
|
// print(forceOperator);
|
||||||
|
// println("\"");
|
||||||
|
//
|
||||||
|
// if (readResponse() != ResponseOK) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!waitForSignalQuality()) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!attachGprs()) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// If we got this far we succeeded
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Sodaq_nbIOT::createSocket(uint16_t localPort) {
|
||||||
|
// only Datagram/UDP is supported
|
||||||
|
print("AT+NSOCR=DGRAM,17,");
|
||||||
|
print(localPort);
|
||||||
|
println(",1"); // enable incoming message URC (NSONMI)
|
||||||
|
delay(500);
|
||||||
|
|
||||||
|
uint8_t socket;
|
||||||
|
|
||||||
|
if (readResponse<uint8_t, uint8_t>(_createSocketParser, &socket, NULL) == ResponseOK) {
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SOCKET_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResponseTypes Sodaq_nbIOT::_createSocketParser(ResponseTypes &response, const char *buffer, size_t size,
|
||||||
|
uint8_t *socket, uint8_t *dummy) {
|
||||||
|
|
||||||
|
if (!socket) {
|
||||||
|
return ResponseError;
|
||||||
|
}
|
||||||
|
|
||||||
|
int value;
|
||||||
|
|
||||||
|
if (sscanf(buffer, "%d", &value) == 1) {
|
||||||
|
*socket = value;
|
||||||
|
return ResponseEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResponseError;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sodaq_nbIOT::sendSocket(uint8_t socket, const char *host, uint16_t port, const char *buffer, size_t size) {
|
||||||
|
print("AT+NSOST=");
|
||||||
|
print(socket);
|
||||||
|
print(",");
|
||||||
|
print(host);
|
||||||
|
print(",");
|
||||||
|
print(port);
|
||||||
|
print(",");
|
||||||
|
print(size); // why size? not encoded hex size? no idea...
|
||||||
|
print(",");
|
||||||
|
for (uint16_t i = 0; i < size; ++i) {
|
||||||
|
print(static_cast<char>(NIBBLE_TO_HEX_CHAR(HIGH_NIBBLE(buffer[i]))));
|
||||||
|
print(static_cast<char>(NIBBLE_TO_HEX_CHAR(LOW_NIBBLE(buffer[i]))));
|
||||||
|
}
|
||||||
|
println();
|
||||||
|
|
||||||
|
delay(AT_CMD_SLEEP_TIME);
|
||||||
|
|
||||||
|
uint8_t sent;
|
||||||
|
return readResponse<uint8_t, uint8_t>(_sendSocketParser, NULL, &sent) == ResponseOK && sent > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResponseTypes Sodaq_nbIOT::_sendSocketParser(ResponseTypes &response, const char *buffer, size_t size,
|
||||||
|
uint8_t *socket, uint8_t *sent) {
|
||||||
|
if (!socket) {
|
||||||
|
return ResponseError;
|
||||||
|
}
|
||||||
|
|
||||||
|
int value;
|
||||||
|
int value2;
|
||||||
|
|
||||||
|
if (sscanf(buffer, "%d,%d", &value, &value2) == 1) {
|
||||||
|
*sent = value2;
|
||||||
|
|
||||||
|
return ResponseEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResponseError;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Sodaq_nbIOT::socketReceive(uint8_t socket, char *buffer, size_t size) {
|
||||||
|
print("AT+NSORF=");
|
||||||
|
print(socket);
|
||||||
|
print(",");
|
||||||
|
println(size);
|
||||||
|
|
||||||
|
if (readResponse<uint8_t, char>(_socketReceiveParser, &socket, buffer, &size) == ResponseOK) {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResponseTypes Sodaq_nbIOT::_socketReceiveParser(ResponseTypes &response, const char *buffer, size_t size,
|
||||||
|
uint8_t *socket, char *parsedBuffer) {
|
||||||
|
if (!socket) {
|
||||||
|
return ResponseError;
|
||||||
|
}
|
||||||
|
|
||||||
|
// <socket>,<ip_addr>,<port>,<length>,<data>,<remaining_length>
|
||||||
|
if (sscanf(buffer, "%d,%s,%d,%d,%s,%d", NULL, NULL, NULL, NULL, parsedBuffer, NULL) == 1) {
|
||||||
|
return ResponseEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResponseError;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Sodaq_nbIOT::socketBytesPending(uint8_t socket) {
|
||||||
|
|
||||||
|
int count = readLn(_inputBuffer, _inputBufferSize, 250);
|
||||||
|
|
||||||
|
if (count == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int inc_socket;
|
||||||
|
int length;
|
||||||
|
|
||||||
|
if (startsWith("+NSONMI:", _inputBuffer)) {
|
||||||
|
sscanf(_inputBuffer, "+NSONMI:%d,%d", &inc_socket, &length);
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sodaq_nbIOT::closeSocket(uint8_t socket) {
|
||||||
|
print("AT+NSOCL=");
|
||||||
|
println(socket);
|
||||||
|
return readResponse() == ResponseOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disconnects the modem from the network.
|
||||||
|
bool Sodaq_nbIOT::disconnect() {
|
||||||
|
println("AT+CGATT=0");
|
||||||
|
|
||||||
|
return (readResponse(NULL, 40000) == ResponseOK);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the modem is connected to the network and has an activated data connection.
|
||||||
|
bool Sodaq_nbIOT::isConnected() {
|
||||||
|
uint8_t value = 0;
|
||||||
|
|
||||||
|
println("AT+CGATT?");
|
||||||
|
|
||||||
|
if (readResponse<uint8_t, uint8_t>(_cgattParser, &value, NULL) == ResponseOK) {
|
||||||
|
return (value == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets the Received Signal Strength Indication in dBm and Bit Error Rate.
|
||||||
|
// Returns true if successful.
|
||||||
|
bool Sodaq_nbIOT::getRSSIAndBER(int8_t *rssi, uint8_t *ber) {
|
||||||
|
static char berValues[] = {49, 43, 37, 25, 19, 13, 7, 0}; // 3GPP TS 45.008 [20] subclause 8.2.4
|
||||||
|
|
||||||
|
println("AT+CSQ");
|
||||||
|
|
||||||
|
int csqRaw = 0;
|
||||||
|
int berRaw = 0;
|
||||||
|
|
||||||
|
if (readResponse<int, int>(_csqParser, &csqRaw, &berRaw) == ResponseOK) {
|
||||||
|
*rssi = ((csqRaw == 99) ? 0 : convertCSQ2RSSI(csqRaw));
|
||||||
|
*ber = ((berRaw == 99 || static_cast<size_t>(berRaw) >= sizeof(berValues)) ? 0 : berValues[berRaw]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
The range is the following:
|
||||||
|
0: -113 dBm or less
|
||||||
|
1: -111 dBm
|
||||||
|
2..30: from -109 to -53 dBm with 2 dBm steps
|
||||||
|
31: -51 dBm or greater
|
||||||
|
99: not known or not detectable or currently not available
|
||||||
|
*/
|
||||||
|
int8_t Sodaq_nbIOT::convertCSQ2RSSI(uint8_t csq) const {
|
||||||
|
return -113 + 2 * csq;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t Sodaq_nbIOT::convertRSSI2CSQ(int8_t rssi) const {
|
||||||
|
return (rssi + 113) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sodaq_nbIOT::startsWith(const char *pre, const char *str) {
|
||||||
|
return (strncmp(pre, str, strlen(pre)) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Sodaq_nbIOT::ipToString(IP_t ip, char *buffer, size_t size) {
|
||||||
|
return snprintf(buffer, size, IP_FORMAT, IP_TO_TUPLE(ip));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sodaq_nbIOT::isValidIPv4(const char *str) {
|
||||||
|
uint8_t segs = 0; // Segment count
|
||||||
|
uint8_t chcnt = 0; // Character count within segment
|
||||||
|
uint8_t accum = 0; // Accumulator for segment
|
||||||
|
|
||||||
|
if (!str) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process every character in string
|
||||||
|
while (*str != '\0') {
|
||||||
|
// Segment changeover
|
||||||
|
if (*str == '.') {
|
||||||
|
// Must have some digits in segment
|
||||||
|
if (chcnt == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit number of segments
|
||||||
|
if (++segs == 4) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset segment values and restart loop
|
||||||
|
chcnt = accum = 0;
|
||||||
|
str++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check numeric
|
||||||
|
if ((*str < '0') || (*str > '9')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accumulate and check segment
|
||||||
|
if ((accum = accum * 10 + *str - '0') > 255) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance other segment specific stuff and continue loop
|
||||||
|
chcnt++;
|
||||||
|
str++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check enough segments and enough characters in last segment
|
||||||
|
if (segs != 3) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chcnt == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Address OK
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sodaq_nbIOT::waitForSignalQuality(uint32_t timeout) {
|
||||||
|
uint32_t start = millis();
|
||||||
|
const int8_t minRSSI = getMinRSSI();
|
||||||
|
int8_t rssi;
|
||||||
|
uint8_t ber;
|
||||||
|
|
||||||
|
uint32_t delay_count = 500;
|
||||||
|
|
||||||
|
while (!is_timedout(start, timeout)) {
|
||||||
|
if (getRSSIAndBER(&rssi, &ber)) {
|
||||||
|
if (rssi != 0 && rssi >= minRSSI) {
|
||||||
|
_lastRSSI = rssi;
|
||||||
|
_CSQtime = (int32_t) (millis() - start) / 1000;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sodaq_wdt_safe_delay(delay_count);
|
||||||
|
|
||||||
|
// Next time wait a little longer, but not longer than 5 seconds
|
||||||
|
if (delay_count < 5000) {
|
||||||
|
delay_count += 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResponseTypes
|
||||||
|
Sodaq_nbIOT::_cgattParser(ResponseTypes &response, const char *buffer, size_t size, uint8_t *result, uint8_t *dummy) {
|
||||||
|
if (!result) {
|
||||||
|
return ResponseError;
|
||||||
|
}
|
||||||
|
|
||||||
|
int val;
|
||||||
|
if (sscanf(buffer, "+CGATT: %d", &val) == 1) {
|
||||||
|
*result = val;
|
||||||
|
return ResponseEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResponseError;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResponseTypes Sodaq_nbIOT::_csqParser(ResponseTypes &response, const char *buffer, size_t size,
|
||||||
|
int *rssi, int *ber) {
|
||||||
|
if (!rssi || !ber) {
|
||||||
|
return ResponseError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sscanf(buffer, "+CSQ: %d,%d", rssi, ber) == 2) {
|
||||||
|
return ResponseEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResponseError;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sodaq_nbIOT::sendMessage(const uint8_t *buffer, size_t size) {
|
||||||
|
if (size > 512) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
print("AT+NMGS=");
|
||||||
|
print(size);
|
||||||
|
print(",");
|
||||||
|
|
||||||
|
for (uint16_t i = 0; i < size; ++i) {
|
||||||
|
print(static_cast<char>(NIBBLE_TO_HEX_CHAR(HIGH_NIBBLE(buffer[i]))));
|
||||||
|
print(static_cast<char>(NIBBLE_TO_HEX_CHAR(LOW_NIBBLE(buffer[i]))));
|
||||||
|
}
|
||||||
|
|
||||||
|
println();
|
||||||
|
|
||||||
|
return (readResponse() == ResponseOK);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Sodaq_nbIOT::getSentMessagesCount(SentMessageStatus filter) {
|
||||||
|
println("AT+NQMGS");
|
||||||
|
|
||||||
|
uint16_t pendingCount = 0;
|
||||||
|
uint16_t errorCount = 0;
|
||||||
|
|
||||||
|
if (readResponse<uint16_t, uint16_t>(_nqmgsParser, &pendingCount, &errorCount) == ResponseOK) {
|
||||||
|
if (filter == Pending) {
|
||||||
|
return pendingCount;
|
||||||
|
} else if (filter == Error) {
|
||||||
|
return errorCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResponseTypes
|
||||||
|
Sodaq_nbIOT::_nqmgsParser(ResponseTypes &response, const char *buffer, size_t size, uint16_t *pendingCount,
|
||||||
|
uint16_t *errorCount) {
|
||||||
|
if (!pendingCount || !errorCount) {
|
||||||
|
return ResponseError;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pendingValue;
|
||||||
|
int errorValue;
|
||||||
|
|
||||||
|
if (sscanf(buffer, "PENDING=%d,SENT=%*d,ERROR=%d", &pendingValue, &errorValue) == 2) {
|
||||||
|
*pendingCount = pendingValue;
|
||||||
|
*errorCount = errorValue;
|
||||||
|
|
||||||
|
return ResponseEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResponseError;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sodaq_nbIOT::sendMessage(const char *str) {
|
||||||
|
return sendMessage((const uint8_t *) str, strlen(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sodaq_nbIOT::sendMessage(String str) {
|
||||||
|
return sendMessage(str.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==============================
|
||||||
|
// on/off class
|
||||||
|
// ==============================
|
||||||
|
Sodaq_nbIotOnOff::Sodaq_nbIotOnOff() {
|
||||||
|
_onoffPin = -1;
|
||||||
|
_onoff_status = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initializes the instance
|
||||||
|
void Sodaq_nbIotOnOff::init(int onoffPin) {
|
||||||
|
if (onoffPin >= 0) {
|
||||||
|
_onoffPin = onoffPin;
|
||||||
|
// First write the output value, and only then set the output mode.
|
||||||
|
digitalWrite(_onoffPin, LOW);
|
||||||
|
pinMode(_onoffPin, OUTPUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sodaq_nbIotOnOff::on() {
|
||||||
|
if (_onoffPin >= 0) {
|
||||||
|
digitalWrite(_onoffPin, HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onoff_status = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sodaq_nbIotOnOff::off() {
|
||||||
|
// The GPRSbee is switched off immediately
|
||||||
|
if (_onoffPin >= 0) {
|
||||||
|
digitalWrite(_onoffPin, LOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should be instant
|
||||||
|
// Let's wait a little, but not too long
|
||||||
|
delay(50);
|
||||||
|
_onoff_status = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sodaq_nbIotOnOff::isOn() {
|
||||||
|
#if defined(ARDUINO_ARCH_AVR)
|
||||||
|
// Use the onoff pin, which is close to useless
|
||||||
|
bool status = digitalRead(_onoffPin);
|
||||||
|
return status;
|
||||||
|
#elif defined(ARDUINO_ARCH_SAMD)
|
||||||
|
// There is no status pin. On SAMD we cannot read back the onoff pin.
|
||||||
|
// So, our own status is all we have.
|
||||||
|
return _onoff_status;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Let's assume it is on.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
165
src/Sodaq_nbIOT.h
Normal file
165
src/Sodaq_nbIOT.h
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2015-2016 Sodaq. All rights reserved.
|
||||||
|
|
||||||
|
This file is part of Sodaq_nbIOT.
|
||||||
|
|
||||||
|
Sodaq_nbIOT is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of
|
||||||
|
the License, or(at your option) any later version.
|
||||||
|
|
||||||
|
Sodaq_nbIOT is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with Sodaq_nbIOT. If not, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _Sodaq_nbIOT_h
|
||||||
|
#define _Sodaq_nbIOT_h
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "Sodaq_AT_Device.h"
|
||||||
|
|
||||||
|
class Sodaq_nbIOT: public Sodaq_AT_Device
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Sodaq_nbIOT();
|
||||||
|
|
||||||
|
enum SentMessageStatus {
|
||||||
|
Pending,
|
||||||
|
Error
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef ResponseTypes(*CallbackMethodPtr)(ResponseTypes& response, const char* buffer, size_t size,
|
||||||
|
void* parameter, void* parameter2);
|
||||||
|
|
||||||
|
bool setRadioActive(bool on);
|
||||||
|
bool setIndicationsActive(bool on);
|
||||||
|
bool setApn(const char* apn);
|
||||||
|
bool setCdp(const char* cdp);
|
||||||
|
|
||||||
|
// Returns true if the modem replies to "AT" commands without timing out.
|
||||||
|
bool isAlive();
|
||||||
|
|
||||||
|
// Returns the default baud rate of the modem.
|
||||||
|
// To be used when initializing the modem stream for the first time.
|
||||||
|
uint32_t getDefaultBaudrate() { return 9600; };
|
||||||
|
|
||||||
|
// Initializes the modem instance. Sets the modem stream and the on-off power pins.
|
||||||
|
void init(Stream& stream, int8_t onoffPin);
|
||||||
|
|
||||||
|
// Turns on and initializes the modem, then connects to the network and activates the data connection.
|
||||||
|
bool connect(const char* apn, const char* cdp, const char* forceOperator = 0);
|
||||||
|
|
||||||
|
// Disconnects the modem from the network.
|
||||||
|
bool disconnect();
|
||||||
|
|
||||||
|
// Returns true if the modem is connected to the network and has an activated data connection.
|
||||||
|
bool isConnected();
|
||||||
|
|
||||||
|
// Gets the Received Signal Strength Indication in dBm and Bit Error Rate.
|
||||||
|
// Returns true if successful.
|
||||||
|
bool getRSSIAndBER(int8_t* rssi, uint8_t* ber);
|
||||||
|
int8_t convertCSQ2RSSI(uint8_t csq) const;
|
||||||
|
uint8_t convertRSSI2CSQ(int8_t rssi) const;
|
||||||
|
|
||||||
|
void setMinRSSI(int rssi) { _minRSSI = rssi; }
|
||||||
|
void setMinCSQ(int csq) { _minRSSI = convertCSQ2RSSI(csq); }
|
||||||
|
int8_t getMinRSSI() const { return _minRSSI; }
|
||||||
|
uint8_t getCSQtime() const { return _CSQtime; }
|
||||||
|
int8_t getLastRSSI() const { return _lastRSSI; }
|
||||||
|
|
||||||
|
bool connectSocket();
|
||||||
|
int createSocket(uint16_t localPort = 0);
|
||||||
|
bool sendSocket(uint8_t socket, const char *host, uint16_t port, const char *buffer, size_t size);
|
||||||
|
size_t socketReceive(uint8_t socket, char* buffer, size_t size);
|
||||||
|
size_t socketBytesPending(uint8_t socket);
|
||||||
|
bool closeSocket(uint8_t socket);
|
||||||
|
|
||||||
|
bool sendMessage(const uint8_t* buffer, size_t size);
|
||||||
|
bool sendMessage(const char* str);
|
||||||
|
bool sendMessage(String str);
|
||||||
|
int getSentMessagesCount(SentMessageStatus filter);
|
||||||
|
protected:
|
||||||
|
// override
|
||||||
|
ResponseTypes readResponse(char* buffer, size_t size, size_t* outSize, uint32_t timeout = SODAQ_AT_DEVICE_DEFAULT_READ_MS)
|
||||||
|
{
|
||||||
|
return readResponse(_inputBuffer, _inputBufferSize, NULL, NULL, NULL, outSize, timeout);
|
||||||
|
};
|
||||||
|
|
||||||
|
ResponseTypes readResponse(char* buffer, size_t size,
|
||||||
|
CallbackMethodPtr parserMethod, void* callbackParameter, void* callbackParameter2 = NULL,
|
||||||
|
size_t* outSize = NULL, uint32_t timeout = SODAQ_AT_DEVICE_DEFAULT_READ_MS);
|
||||||
|
|
||||||
|
ResponseTypes readResponse(size_t* outSize = NULL, uint32_t timeout = SODAQ_AT_DEVICE_DEFAULT_READ_MS)
|
||||||
|
{
|
||||||
|
return readResponse(_inputBuffer, _inputBufferSize, NULL, NULL, NULL, outSize, timeout);
|
||||||
|
};
|
||||||
|
|
||||||
|
ResponseTypes readResponse(CallbackMethodPtr parserMethod, void* callbackParameter,
|
||||||
|
void* callbackParameter2 = NULL, size_t* outSize = NULL, uint32_t timeout = SODAQ_AT_DEVICE_DEFAULT_READ_MS)
|
||||||
|
{
|
||||||
|
return readResponse(_inputBuffer, _inputBufferSize,
|
||||||
|
parserMethod, callbackParameter, callbackParameter2,
|
||||||
|
outSize, timeout);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T1, typename T2>
|
||||||
|
ResponseTypes readResponse(ResponseTypes(*parserMethod)(ResponseTypes& response, const char* parseBuffer, size_t size, T1* parameter, T2* parameter2),
|
||||||
|
T1* callbackParameter, T2* callbackParameter2,
|
||||||
|
size_t* outSize = NULL, uint32_t timeout = SODAQ_AT_DEVICE_DEFAULT_READ_MS)
|
||||||
|
{
|
||||||
|
return readResponse(_inputBuffer, _inputBufferSize, (CallbackMethodPtr)parserMethod,
|
||||||
|
(void*)callbackParameter, (void*)callbackParameter2, outSize, timeout);
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
//uint16_t _socketPendingBytes[SOCKET_COUNT]; // TODO add getter
|
||||||
|
//bool _socketClosedBit[SOCKET_COUNT];
|
||||||
|
|
||||||
|
// This is the value of the most recent CSQ
|
||||||
|
// Notice that CSQ is somewhat standard. SIM800/SIM900 and Ublox
|
||||||
|
// compute to comparable numbers. With minor deviations.
|
||||||
|
// For example SIM800
|
||||||
|
// 1 -111 dBm
|
||||||
|
// 2...30 -110... -54 dBm
|
||||||
|
// For example UBlox
|
||||||
|
// 1 -111 dBm
|
||||||
|
// 2..30 -109 to -53 dBm
|
||||||
|
int8_t _lastRSSI; // 0 not known or not detectable
|
||||||
|
|
||||||
|
// This is the number of second it took when CSQ was record last
|
||||||
|
uint8_t _CSQtime;
|
||||||
|
|
||||||
|
// This is the minimum required RSSI to continue making the connection
|
||||||
|
// Use convertCSQ2RSSI if you have a CSQ value
|
||||||
|
int _minRSSI;
|
||||||
|
|
||||||
|
static bool startsWith(const char* pre, const char* str);
|
||||||
|
static size_t ipToString(IP_t ip, char* buffer, size_t size);
|
||||||
|
static bool isValidIPv4(const char* str);
|
||||||
|
|
||||||
|
bool waitForSignalQuality(uint32_t timeout = 60L * 1000);
|
||||||
|
bool attachGprs(uint32_t timeout = 30 * 1000);
|
||||||
|
bool setNconfigParam(const char* param, const char* value);
|
||||||
|
bool checkAndApplyNconfig();
|
||||||
|
void reboot();
|
||||||
|
|
||||||
|
static ResponseTypes _csqParser(ResponseTypes& response, const char* buffer, size_t size, int* rssi, int* ber);
|
||||||
|
static ResponseTypes _createSocketParser(ResponseTypes& response, const char* buffer, size_t size,
|
||||||
|
uint8_t* socket, uint8_t* dummy);
|
||||||
|
static ResponseTypes _sendSocketParser(ResponseTypes &response, const char *buffer, size_t size,
|
||||||
|
uint8_t *socket, uint8_t *sent);
|
||||||
|
static ResponseTypes _socketReceiveParser(ResponseTypes& response, const char* buffer, size_t size,
|
||||||
|
uint8_t* socket, char* parsedBuffer);
|
||||||
|
static ResponseTypes _nqmgsParser(ResponseTypes& response, const char* buffer, size_t size, uint16_t* pendingCount, uint16_t* errorCount);
|
||||||
|
static ResponseTypes _cgattParser(ResponseTypes& response, const char* buffer, size_t size, uint8_t* result, uint8_t* dummy);
|
||||||
|
static ResponseTypes _nconfigParser(ResponseTypes& response, const char* buffer, size_t size, bool* nconfigEqualsArray, uint8_t* dummy);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user