Skip to content
ajaygarg84 edited this page May 29, 2017 · 182 revisions

Beginnings

Please visit http://support.sensegrow.com/discussions/forums/14000092311 for a public-platform for discussions on instamsg-c.

Purpose

This page lists down the steps to port an existing InstaMsg-application on a new device.
In particular, this page lists down the steps to port following two apps ::

  • publisher, which publishes MQTT-messages on a topic.
  • subscriber, which subscribes on the same topic, and receives the messages whenever publisher publishes MQTT-messages on that topic.

Following is the flowchart-schematic of the message-flow we intend to setup, after our new-device is integrated :

During the process of porting the above, the InstaMsg-Kernel would be prepared. Thereafter, the same InstaMsg-Kernel code would be usable by any app.

In other words, after we have successfully completed the above "Hello World" device-communication-scenario, none of the other apps need to worry about implementing the device-specific InstaMsg-Kernel code.

In general, if we wish to build a binary for application titled app for a device titled device, simple following command needs to be run

./build_binary.sh app device

There is a many-to-many correspondence between app and device. That is,

  • An app once written can be made to run on any device.
  • Once the device specific InstaMsg-Kernel code is written, it can run any business-logic of any app

So, in the process of porting our "Hello World", we are completing the device part (for the device in hand) of the apps-devices ecosystem.

Overview of the code-architecture

Every application-code is divided into two categories ::

  • Common code, which is completely device-agnostic.
    The device-implementors must not worry of this section.

  • Device-Specific code, which needs to be implemented per-device.
    This code is primarily concerned with the following interface-implementations

    • Socket/Network/Connectivity functionality, that allows the device to connect to the Internet.
    • Logging functiality, that helps us track what the binary is doing :)
    • Timing functionality (mainly providing a minimal delay-method)

    As is obvious, above functionalities are very device-specific, and thus (obviously) there can be no one code for the above interfaces that runs on all devices.

    For example, some devices might have a SIM-card-based-GPRS-interface to connect to Internet, while some might have a vanilla wifi-module embedded on the SOC.

    Some devices might possess a full-fledged file-system built-in, while some might only possess the luxury of a UART-based-serial-logger.

    Nearly every device (at least in the embedded-world) has its own unique way to provide timing-functionalities.

So, all in all, in the process of porting, we will be concerned in implementing the Device-Specific code for the device in hand.

Ultimately, a single-binary would be built, by compiling-and-linking the code of both the Common and Device-Specific sections together.

Important things to know

  • Common code is 100% bare-metal C.

This means that this section of code is compilable on any device with a bare-metal-C compiler, and does not require any fancy OS-libraries.

This inherently means that above code is compilable on any C-compiler (since every C-compiler, however small or advanced, has to support bare-metal-C rules).

  • Common code is 100% Single-Threaded Code.

This is in keeping with the mantra of sticking to bare-metal-C (because multi-threading libraries are very device-specific, with some devices not having the luxury of multi-threading at all).

  • Device-Specific code-implementors are free to choose whatever libraries they desire.

The only thing that should be remembered is that the entry- and exit-points of every device-specific code-method must be wrapped in a pseudo-synchronous context.

For example, a device might choose to implement a method get_modem_serial_number through a AT-command which responds in an (asynchronous) interrupt-driven manner. This is fine, as long as the Common driver/callee code receives the result of get_modem_serial_number in a pseudo-synchronous manner.

Steps

##1) Clone the repository

git clone git@github.com:InstaMsg/instamsg-c.git
cd instamsg-c

##2) Create a folder for the new-device, in which the Device-Specific code will be implemented.

cp -r device/stub/ device/test_device

##3)
Open the file

vim device/test_device/instamsg/Makefile

and change the following to the compiler that is to be used for compiling-code for this device.

# Compile-cum-Link command for the platform.
#
# Please note that compilation-and-linking is done all in one step
# (and not in two separate "compile" and "link" steps).
#
COMPILE_COMMAND="gcc "

Also, if the device is an embedded-device, the code would also need to be flashed on the device. That can be done by specifying the extra commands via the following section ::

# Any final commands that need to be run.
#
# Most-likely, these commands would (prepare to) flash the
# binaries on the end-device.
#
# For this, these commands would need to require the location
# of the output-directory of "instamsg" binary.
#
# Whenever this is required, use OUT_DIR (without any dollar
# sign or anything), and the main builder-script will take
# care of that.
#
# For example,
#
# device_flashing_binary OUT_DIR/instamsg /dev/devID
#
#declare -a FINAL_COMMANDS=("command-1" "command-2" "and" "so" "on")

##4) Choose the implementation of "sg_sprintf", by defining the proper value in device/test_device/instamsg/device_defines.h

The default implementation (built-in into InstaMsg) can be seen in device/stub/instamsg/device_defines.h. A device-specific implementation-reference has been defined device/linux_desktop/instamsg/device_defines.h

Also, define other values as appropriate to the platform, in device/test_device/instamsg/device_defines.h.

##5)
Next, run the following two commands, which compile the "publisher" and "subscriber" binaries for test_device.

./build_binary.sh publisher test_device
./build_binary.sh subscriber test_device

Both should succeed; however functions in following files need to be implemented. In particular, methods in the following files must be implemented first of all ::

  • device/test_device/instamsg/device_time.c

  • device/test_device/instamsg/device_file_system.c
    (for debug-logging on file-system-based systems)

  • device/test_device/instamsg/device_serial_logger.c
    (for debug-logging on non-file-system-based systems)

Thereafter, following modules must be implemented ::

  • device/test_device/instamsg/device_at.c (for systems with AT_INTERFACE_ENABLED = 1)
  • device/test_device/instamsg/device_misc.c
  • device/test_device/instamsg/device_socket.c
  • device/test_device/instamsg/device_config.c
  • device/test_device/instamsg/device_watchdog.c
  • device/test_device/instamsg/device_data_logger.c

##6) In the following method in device/test_device/instamsg/device_misc.c

void get_device_uuid(char *buffer, int maxbufferlength)

The above method must fill in a truly-universally-unique-value for each device in "buffer".

Let's say the UUIDs returned are ::

TEST-DEVICE:UUID:Key-1 for instance-1
TEST-DEVICE:UUID:Key-2 for instance-2

Also,

void get_prov_pin_for_non_gsm_devices(char *buffer, int maxbufferlength)

The above method must fill in a first-time provisioning-key into "buffer".

Let's say the keys returned are ::

key-1 for instance-1
key-2 for instance-2

##7)

Provision the above two devices, using the corresponding value returned from step-9 as the Provisioning-Id, and the (Re-)Activate Provisioning fields. Please refer the following link for provisioning-details ::

Provisioning Device On InstaMsg-Server

Please remember to specifiy the following, while provisioning the client-devices ::

  • Instance-1 (provisioning-id = TEST-DEVICE:UUID:Key-1) must have listener_topic as one of the Sub-topics.
  • Instance-2 (provisioning-id = TEST-DEVICE:UUID:Key-2) must have listener_topic as one of the Pub-topics.

##8) Re-compile the binaries, as per step 5).

##9) On first instance of the particular test_device, run the subscriber binary as ::

./build/subscriber/test_device/instamsg

##10) On second instance of the particular test_device, run the publisher binary as ::

./build/publisher/test_device/instamsg

##11) Finally, back to first instance running the subscriber, we observe that the messages published from publisher are received on subscriber ::

Test 1
Test 2
Time to play ping-pong with server !!!

PINGRESP received... relations are intact !!

Test 3
Test 4
Test 5

Note that following intermediate-logs like

Time to play ping-pong with server !!!
PINGRESP received... relations are intact !!

will be observed periodically, as this is a heartbeat-mechanism between the device-client and instamsg-server (via exchange of MQTT-PINGREQ and MQTT-PINGRESP messages).

#User-APIs

#Writing new-application

With the InstaMsg-Kernel APIs implemented as part of the above publisher/subscriber binary, new application can be simply written by calling the following method ::

Application-Starter-Method

Kindly view following sample applications bundled with InstaMsg. In particular, these applications work best in pairs ::

Clone this wiki locally