authorNathael Pajani <nathael.pajani@ed3l.fr>
Sat, 16 Mar 2019 17:52:04 +0000 (18:52 +0100)
committerNathael Pajani <nathael.pajani@ed3l.fr>
Sat, 16 Mar 2019 17:52:04 +0000 (18:52 +0100)
apps/base/mqtt_pub/Makefile [new file with mode: 0644]
apps/base/mqtt_pub/README [new file with mode: 0644]
apps/base/mqtt_pub/main.c [new file with mode: 0644]
apps/base/mqtt_pub/mqtt_comm.c [new file with mode: 0644]
apps/base/mqtt_pub/mqtt_comm.h [new file with mode: 0644]

diff --git a/apps/base/mqtt_pub/Makefile b/apps/base/mqtt_pub/Makefile
new file mode 100644 (file)
index 0000000..41ae555
--- /dev/null
@@ -0,0 +1,21 @@
+# Makefile for apps
+
+MODULE = $(shell basename $(shell cd .. && pwd && cd -))
+NAME = $(shell basename $(CURDIR))
+
+# Add this to your ~/.vimrc in order to get proper function of :make in vim :
+# let $COMPILE_FROM_IDE = 1
+ifeq ($(strip $(COMPILE_FROM_IDE)),)
+       PRINT_DIRECTORY = --no-print-directory
+else
+       PRINT_DIRECTORY =
+       LANG = C
+endif
+
+.PHONY: $(NAME).bin
+$(NAME).bin:
+       @make -C ../../.. ${PRINT_DIRECTORY} NAME=$(NAME) MODULE=$(MODULE) apps/$(MODULE)/$(NAME)/$@
+
+clean mrproper:
+       @make -C ../../.. ${PRINT_DIRECTORY} $@
+
diff --git a/apps/base/mqtt_pub/README b/apps/base/mqtt_pub/README
new file mode 100644 (file)
index 0000000..181b390
--- /dev/null
@@ -0,0 +1,48 @@
+MQTT protocol example, Publisher part
+
+Copyright 2019 Nathael Pajani <nathael.pajani@ed3l.fr>
+
+
+/* ****************************************************************************
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+This example shows the support of the publisher part ot he MQTT protocol
+implementation provided in lib/protocols/mqtt.c
+
+This implementation has been designed to implement only the MQTT packet
+building and decoding, as specified by the MQTT protocol specification, and
+leave all the MQTT message flow implementation to the application, as it is not
+part of the MQTT protocol specification and is application dependent.
+
+One can thus implement either a simple "single in-flight packet" mechanism for
+very lightweight applications or complex multi-packet mechanisms.
+
+This example uses the simple "single in-flight packet" mechanism to publish
+the temperature read from the onboard temperature sensor.
+
+As MQTT requires a lossless, ordered, addressable transport protocol, we used a
+simple protocol over serial communication, which would allow communication of
+multiple "clients" over an RS485 serial link.
+This protocol is decoded by the bridge example provided in host/mqtt_bridge,
+which simply transfers the MQTT part of received packets on a TCP socket
+connected to an MQTT message broker, and encapsulate MQTT packets received on
+ths socket in our simple protocol before sending them on the serial link.
+
+This implementation is very simple, and does not try to resend packets which
+did not get acked (QoS 0) even if they are sent with a QoS of 1 in order to
+test and demonstrate the acknowledge part of publish messages.
+It will be improved some day in order to implement full QoS support and test
+QoS level 2.
diff --git a/apps/base/mqtt_pub/main.c b/apps/base/mqtt_pub/main.c
new file mode 100644 (file)
index 0000000..3fd39c1
--- /dev/null
@@ -0,0 +1,209 @@
+/****************************************************************************
+ *   apps/base/mqtt_pub/main.c
+ *
+ * MQTT client example using data from onboard TMP101 I2C temperature sensor
+ *
+ * Copyright 2013-2014 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+
+#include "core/system.h"
+#include "core/systick.h"
+#include "core/pio.h"
+
+#include "lib/stdio.h"
+#include "lib/errno.h"
+#include "lib/utils.h"
+#include "lib/protocols/mqtt.h"
+
+#include "drivers/i2c.h"
+#include "drivers/serial.h"
+#include "drivers/gpio.h"
+
+#include "extdrv/status_led.h"
+#include "extdrv/tmp101_temp_sensor.h"
+
+#include "mqtt_comm.h"
+
+#define MODULE_VERSION    0x04
+#define MODULE_NAME "GPIO Demo Module"
+
+#define SELECTED_FREQ  FREQ_SEL_48MHz
+
+/***************************************************************************** */
+/* Pins configuration */
+/* pins blocks are passed to set_pins() for pins configuration.
+ * Unused pin blocks can be removed safely with the corresponding set_pins() call
+ * All pins blocks may be safelly merged in a single block for single set_pins() call..
+ */
+const struct pio_config common_pins[] = {
+       /* UART 0 */
+       { LPC_UART0_RX_PIO_0_1,  LPC_IO_DIGITAL },
+       { LPC_UART0_TX_PIO_0_2,  LPC_IO_DIGITAL },
+       /* I2C 0 */
+       { LPC_I2C0_SCL_PIO_0_10, (LPC_IO_DIGITAL | LPC_IO_OPEN_DRAIN_ENABLE) },
+       { LPC_I2C0_SDA_PIO_0_11, (LPC_IO_DIGITAL | LPC_IO_OPEN_DRAIN_ENABLE) },
+       ARRAY_LAST_PIO,
+};
+
+const struct pio status_led_green = LPC_GPIO_1_4;
+const struct pio status_led_red = LPC_GPIO_1_5;
+
+/***************************************************************************** */
+/* Temperature */
+/* The I2C Temperature sensor is at address 0x94 */
+#define TMP101_ADDR  0x94  /* Pin Addr0 (pin5 of tmp101) connected to VCC */
+struct tmp101_sensor_config tmp101_sensor = {
+       .bus_num = I2C0,
+       .addr = TMP101_ADDR,
+       .resolution = TMP_RES_ELEVEN_BITS,
+};
+
+void temp_config(int uart_num)
+{
+       int ret = 0;
+
+       /* Temp sensor */
+       ret = tmp101_sensor_config(&tmp101_sensor);
+       if (ret != 0) {
+               uprintf(uart_num, "Temp config error: %d\n", ret);
+       }
+}
+
+int temp_read(int uart_num)
+{
+       uint16_t raw = 0;
+       int deci_degrees = 0;
+       int ret = 0;
+
+       tmp101_sensor_start_conversion(&tmp101_sensor);
+       msleep(250); /* Wait for the end of the conversion : 40ms */
+       ret = tmp101_sensor_read(&tmp101_sensor, &raw, &deci_degrees);
+       if (ret != 0) {
+               uprintf(uart_num, "Temp read error: %d\n", ret);
+       } else {
+               uprintf(uart_num, "Temp read: %d,%d - raw: 0x%04x.\r\n",
+                               (deci_degrees/10), (deci_degrees%10), raw);
+       }
+       return deci_degrees;
+}
+
+
+uint8_t rx_packets[2][MQTT_BUFF_SIZE];
+volatile uint8_t* rxbuf = rx_packets[0];
+volatile uint8_t* packet_ok = NULL;
+void data_rx(uint8_t c)
+{
+       static uint8_t idx = 0;
+       static uint8_t sum = 0;
+       /* Not receiving a packet yet, wait for FIRST_PACKET_CHAR, drop */
+       if ((idx == 0) && (c != FIRST_PACKET_CHAR)) {
+               return;
+       }
+       rxbuf[idx++] = c;
+       sum += c;
+       /* If header received, make sure checksum is OK */
+       if (idx == HEADER_SIZE) {
+               uint8_t start = 1;
+               if (sum == 0) {
+                       /* Header received and checksum OK */
+                       return;
+               }
+               /* Shift buffer content if buffer holds a FIRST_PACKET_CHAR */
+               while ((rxbuf[start] != FIRST_PACKET_CHAR) && (start <= HEADER_SIZE)) {
+                       start++;
+               }
+               idx = 0; /* Restart at 0 (and if no FIRST_PACKET_CHAR found, this drops everything */
+               sum = 0;
+               /* FIRST_PACKET_CHAR found ? */
+               while (start < HEADER_SIZE) {
+                       rxbuf[idx] = rxbuf[start++];
+                       sum += rxbuf[idx];
+                       idx++;
+               }
+       } else if (idx > HEADER_SIZE) {
+               struct app_header* header = (struct app_header*)rxbuf;
+               /* Header received, check for data part */
+               if (idx == (HEADER_SIZE + header->data_len)) {
+                       /* Full packet received, move to next packet */
+                       if (sum == header->data_cksum) {
+                               packet_ok = rxbuf;
+                               if (rxbuf == rx_packets[0]) {
+                                       rxbuf = rx_packets[1];
+                               } else {
+                                       rxbuf = rx_packets[0];
+                               }
+                       }
+                       idx = 0;
+                       sum = 0;
+               }
+       }
+}
+
+/***************************************************************************** */
+void system_init()
+{
+       /* Stop the watchdog */
+       startup_watchdog_disable(); /* Do it right now, before it gets a chance to break in */
+       system_brown_out_detection_config(0); /* No ADC used */
+       system_set_default_power_state();
+       clock_config(SELECTED_FREQ);
+       set_pins(common_pins);
+       gpio_on();
+       status_led_config(&status_led_green, &status_led_red);
+       /* System tick timer MUST be configured and running in order to use the sleeping
+        * functions */
+       systick_timer_on(1); /* 1ms */
+       systick_start();
+}
+
+
+/***************************************************************************** */
+int main(void)
+{
+       int temp = 0, loop = 0;
+
+       system_init();
+       uart_on(UART0, 115200, data_rx);
+       i2c_on(I2C0, I2C_CLK_100KHz, I2C_MASTER);
+
+       /* Configure onboard temp sensor */
+       temp_config(UART0);
+
+       /* Connect to brocker and start MQTT protocol handling */
+       mqtt_init(UART0, UART0);
+
+       while (1) {
+               /* Check for received packet on serial line and handle them */
+               if (packet_ok != NULL) {
+                       int ret = mqtt_handle_packet((char*)packet_ok, UART0, UART0);
+                       if (ret != 0) {
+                               uprintf(UART0, "Packet handling returned error %d\n", ret);
+                       }
+                       packet_ok = NULL;
+               }
+               chenillard(250);
+               if (loop++ >= 10) {
+                       temp = temp_read(UART0);
+                       mqtt_temp_publish(UART0, UART0, temp);
+                       loop = 0;
+               }
+       }
+       return 0;
+}
+
diff --git a/apps/base/mqtt_pub/mqtt_comm.c b/apps/base/mqtt_pub/mqtt_comm.c
new file mode 100644 (file)
index 0000000..384ad0f
--- /dev/null
@@ -0,0 +1,208 @@
+/****************************************************************************
+ *   apps/base/mqtt_pub/mqtt_comm.c
+ *
+ * MQTT client example using data from onboard TMP101 I2C temperature sensor
+ *
+ * Copyright 2013-2014 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+
+#include "lib/stdio.h"
+#include "lib/errno.h"
+#include "lib/utils.h"
+#include "lib/protocols/mqtt.h"
+
+#include "drivers/serial.h"
+
+#include "mqtt_comm.h"
+
+
+/***************************************************************************** */
+/* MQTT protocol */
+
+/* Our MQTT buffer. MQTT packets may be up to 2^28 bytes, but we will keep them
+ * very small for our needs */
+uint8_t txbuf[MQTT_BUFF_SIZE];
+uint16_t app_board_addr = APP_BOARD_ADDRESS;
+
+/* This is our static connect data */
+const struct mqtt_connect_pkt mqtt_conn = {
+       .keep_alive_seconds = 600,
+       .clean_session_flag = MQTT_SESSION_NEW, /* or MQTT_SESSION_RESUME */
+       .client_id = "test",
+       .will_topic = { .name = "will/temp/test", .QoS = MQTT_QoS_0 },
+       .will_retain = 1,
+       .will_msg_size = 4,
+       .will_msg = (uint8_t*)"Bye",
+       .username = "test",
+       .password = "pass",
+};
+
+uint8_t mqtt_comm_state = MQTT_UNCONNECTED;
+
+
+void add_packet_header(int pkt_len)
+{
+       struct app_header* header = (struct app_header*)txbuf;
+       uint8_t i = 0, cksum = 0;
+
+       /* Compute data checksum */
+       for (i = HEADER_SIZE; i < (HEADER_SIZE + pkt_len); i++) {
+               cksum += txbuf[i];
+       }
+       
+       /* Add the header */
+       header->start = FIRST_PACKET_CHAR;
+       header->type = 'M';
+       header->addr = htons(app_board_addr);
+       header->seqnum = 0;
+       header->data_len = pkt_len;
+       header->data_cksum = cksum;
+       header->header_cksum = 0;
+       /* Compute header chksum */
+       cksum = 0;
+       for (i = 0; i < HEADER_SIZE; i++) {
+               cksum += txbuf[i];
+       }
+       header->header_cksum = (uint8_t)(256 - cksum);
+}
+
+int mqtt_init(int comm_uart, int dbg_uart)
+{
+       int len = 0, ret = 0;
+
+       memset(txbuf, 0, MQTT_BUFF_SIZE);
+       /* Create the MQTT connect packet */
+       len = mqtt_pack_connect_packet(&mqtt_conn, (txbuf + HEADER_SIZE), (MQTT_BUFF_SIZE - HEADER_SIZE));
+       if (len <= 0) {
+               uprintf(dbg_uart, "MQTT connect pkt pack error : %d\n", len);
+               return -E2BIG;
+       }
+       /* Add our packet header */
+       add_packet_header(len);
+
+       /* Send connect MQTT packet */
+       ret = serial_write(comm_uart, (char*)txbuf, (len + HEADER_SIZE));
+       if (ret < 0) {
+               uprintf(dbg_uart, "MQTT send error: %d\n", ret);
+               return ret;
+       }
+       mqtt_comm_state = MQTT_WAITING_CONNECT_ACK;
+       return 0;
+}
+
+
+struct mqtt_publish_pkt pub_pkt = {
+       .topic = { .name = "temp/test", .QoS = 1, },
+       .retain_flag = 1,
+       .message_size = sizeof(int),
+};
+static uint16_t pub_packet_id = 1;
+
+int mqtt_temp_publish(int comm_uart, int dbg_uart, int temp)
+{
+       int len = 0, ret = 0;
+
+       memset(txbuf, 0, MQTT_BUFF_SIZE);
+       /* Create the MQTT publish packet */
+       pub_pkt.packet_id = pub_packet_id;
+       pub_pkt.application_message = (uint8_t*)&temp;
+       len = mqtt_pack_publish_packet(&pub_pkt, (txbuf + HEADER_SIZE), (MQTT_BUFF_SIZE - HEADER_SIZE));
+       if (len <= 0) {
+               uprintf(dbg_uart, "MQTT publish pkt pack error : %d\n", len);
+               return -E2BIG;
+       }
+       /* Add our packet header */
+       add_packet_header(len);
+
+       /* Send publish MQTT packet */
+       ret = serial_write(comm_uart, (char*)txbuf, (len + HEADER_SIZE));
+       if (ret < 0) {
+               uprintf(dbg_uart, "MQTT send error: %d\n", ret);
+               return ret;
+       }
+       mqtt_comm_state = MQTT_WAITING_PUBACK;
+       return 0;
+}
+
+
+int mqtt_handle_packet(char* buf, int comm_uart, int dbg_uart)
+{
+       struct app_header* header = (struct app_header*)buf;
+       int ret = 0;
+
+       /* Decode address */
+       header->addr = ntohs(header->addr);
+       if ((!buf) || (header->addr != APP_BOARD_ADDRESS)) {
+               /* This one is not for us, drop it */
+               uprintf(dbg_uart, "Dropping packet, not for us\n");
+               return -1;
+       }
+       if (header->type != 'M') {
+               uprintf(dbg_uart, "Dropping packet, not MQTT\n");
+               return -2;
+       }
+       /* This is an MQTT packet for us ... handle it */
+       switch (mqtt_comm_state) {
+               case MQTT_WAITING_CONNECT_ACK: {
+                       /* We must not receive anything before the connect acknowledge */
+                       struct mqtt_connack_reply_pkt* reply = (struct mqtt_connack_reply_pkt*)(buf + HEADER_SIZE);
+                       ret = mqtt_check_connack_reply_pkt(reply);
+                       if (ret != 0) {
+                               uprintf(dbg_uart, "Not a connack packet (ret: %d) ... protocol flow error\n", ret);
+                               return -3;
+                       }
+                       /* Valid packet, check return code */
+                       if (reply->ret_code == MQTT_CONNACK_ACCEPTED) {
+                               uprintf(dbg_uart, "Connection accepted\n");
+                               mqtt_comm_state = MQTT_IDLE;
+                       } else {
+                               uprintf(dbg_uart, "Connection refused: %d\n", reply->ret_code);
+                               mqtt_comm_state = MQTT_UNCONNECTED;
+                               return -4;
+                       }
+                       break;
+               }
+               case MQTT_WAITING_PUBACK: {
+                       struct mqtt_publish_reply_pkt* reply = (struct mqtt_publish_reply_pkt*)(buf + HEADER_SIZE);
+                       ret = mqtt_check_publish_reply_pkt(reply, MQTT_CONTROL_PUBACK);
+                       if (ret != 0) {
+                               uprintf(dbg_uart, "Not a puback packet (ret: %d) ... protocol flow error\n", ret);
+                               return -5;
+                       }
+                       if (ntohs(reply->acked_pkt_id) != pub_packet_id) {
+                               uprintf(dbg_uart, "Not a puback for the right packet (got %d, our is %d) ...\n",
+                                                       ntohs(reply->acked_pkt_id), pub_packet_id);
+                               return -6;
+                       }
+                       uprintf(dbg_uart, "Publication accepted\n");
+                       mqtt_comm_state = MQTT_IDLE;
+                       pub_packet_id++;
+                       break;
+               }
+               case MQTT_WAITING_PUBREC:
+               case MQTT_WAITING_PUBREL:
+               case MQTT_WAITING_PUBCOMP:
+               case MQTT_WAITING_SUBACK:
+               case MQTT_WAITING_UNSUBACK:
+               case MQTT_WAITING_PINGRESP:
+                       break;
+       }
+       return 0;
+}
+
diff --git a/apps/base/mqtt_pub/mqtt_comm.h b/apps/base/mqtt_pub/mqtt_comm.h
new file mode 100644 (file)
index 0000000..fbcf49c
--- /dev/null
@@ -0,0 +1,74 @@
+/****************************************************************************
+ *   apps/base/mqtt_pub/mqtt_comm.h
+ *
+ * MQTT client example using data from onboard TMP101 I2C temperature sensor
+ *
+ * Copyright 2013-2014 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#ifndef MQTT_COMM_H
+#define MQTT_COMM_H
+
+#include "lib/stdio.h"
+#include "lib/errno.h"
+#include "lib/utils.h"
+#include "lib/protocols/mqtt.h"
+
+#define APP_BOARD_ADDRESS  0x0102
+
+
+/***************************************************************************** */
+/* MQTT protocol */
+
+/* MQTT packets may be up to 2^28 bytes, but we will keep them very small for our needs */
+#define MQTT_BUFF_SIZE 62
+#define FIRST_PACKET_CHAR  '#'
+
+#define HEADER_SIZE  sizeof(struct app_header)
+struct app_header {
+       uint8_t start; /* '#' */
+       uint8_t type;  /* 'M' for MQTT messages */
+       uint16_t addr;
+       uint8_t seqnum;
+       uint8_t header_cksum;
+       uint8_t data_len;
+       uint8_t data_cksum;
+};
+
+/* Possible states for MQTT communication flow */
+enum mqtt_client_states {
+    MQTT_UNCONNECTED,
+    MQTT_IDLE,
+    MQTT_WAITING_CONNECT_ACK,
+    MQTT_WAITING_PUBACK,
+    MQTT_WAITING_PUBREC,
+    MQTT_WAITING_PUBREL,
+    MQTT_WAITING_PUBCOMP,
+    MQTT_WAITING_SUBACK,
+    MQTT_WAITING_UNSUBACK,
+    MQTT_WAITING_PINGRESP,
+};
+
+int mqtt_init(int comm_uart, int dbg_uart);
+
+int mqtt_temp_publish(int comm_uart, int dbg_uart, int temp);
+
+int mqtt_handle_packet(char* buf, int comm_uart, int dbg_uart);
+
+#endif  /* MQTT_COMM_H */
+