* 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