* initial commit

This commit is contained in:
Danyi Dávid 2017-10-17 21:41:58 +02:00
commit 573b67a58c
12 changed files with 2166 additions and 0 deletions

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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", &param1, &param2) == 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", &param1) == 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
View 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