You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by pr...@apache.org on 2020/10/22 13:17:47 UTC

[incubator-nuttx] 02/03: sensor/driver: wtgahrs2 by serial interface follow sensor.c/sensor.h

This is an automated email from the ASF dual-hosted git repository.

protobits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git

commit a3f978da025e91bb44344b89a3026548d8c494eb
Author: dongjiuzhu <do...@xiaomi.com>
AuthorDate: Mon Oct 19 21:30:22 2020 +0800

    sensor/driver: wtgahrs2 by serial interface follow sensor.c/sensor.h
    
    Wtgahrs2 integrates multiple sensor: accel, gyro, mag, baro and gps.
    
    Signed-off-by: dongjiuzhu <do...@xiaomi.com>
---
 drivers/sensors/Kconfig          |   8 +
 drivers/sensors/Make.defs        |   4 +
 drivers/sensors/wtgahrs2.c       | 578 +++++++++++++++++++++++++++++++++++++++
 include/nuttx/sensors/wtgahrs2.h |  64 +++++
 4 files changed, 654 insertions(+)

diff --git a/drivers/sensors/Kconfig b/drivers/sensors/Kconfig
index f9bad12..2a9bac5 100644
--- a/drivers/sensors/Kconfig
+++ b/drivers/sensors/Kconfig
@@ -11,6 +11,14 @@ menuconfig SENSORS
 
 if SENSORS
 
+config SENSORS_WTGAHRS2
+	bool "Wtgahrs2 Sensor Support"
+	default n
+	---help---
+		We can read sensor data by serial interface. It need the hardware sensor
+		wtgashrs2(JY901) as data source. This sensor can generate accelerometer,
+		gyroscope, magnetic, barometer and gps data.
+
 config SENSORS_APDS9960
 	bool "Avago APDS-9960 Gesture Sensor support"
 	default n
diff --git a/drivers/sensors/Make.defs b/drivers/sensors/Make.defs
index bf87397..a22909f 100644
--- a/drivers/sensors/Make.defs
+++ b/drivers/sensors/Make.defs
@@ -39,6 +39,10 @@ ifeq ($(CONFIG_SENSORS),y)
 
 CSRCS += sensor.c
 
+ifeq ($(CONFIG_SENSORS_WTGAHRS2),y)
+  CSRCS += wtgahrs2.c
+endif
+
 ifeq ($(CONFIG_SENSORS_HCSR04),y)
   CSRCS += hc_sr04.c
 endif
diff --git a/drivers/sensors/wtgahrs2.c b/drivers/sensors/wtgahrs2.c
new file mode 100644
index 0000000..d3311b9
--- /dev/null
+++ b/drivers/sensors/wtgahrs2.c
@@ -0,0 +1,578 @@
+/****************************************************************************
+ * drivers/sensors/wtgahrs2.c
+ * Driver for the Wit-Motion WTGAHRS2 accelerometer, gyroscope, magnetic,
+ * angle, barometer, temperature, gps sensors by serial interface with host
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <nuttx/sensors/wtgahrs2.h>
+#include <nuttx/kthread.h>
+#include <nuttx/kmalloc.h>
+
+#include <termios.h>
+#include <math.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define WTGAHRS2_ARRAYSIZE(a)      (sizeof((a))/sizeof(a[0]))
+
+#define WTGAHRS2_ACCEL_IDX         0
+#define WTGAHRS2_GYRO_IDX          1
+#define WTGAHRS2_MAG_IDX           2
+#define WTGAHRS2_BARO_IDX          3
+#define WTGAHRS2_GPS_IDX           4
+#define WTGAHRS2_MAX_IDX           5
+
+#define WTGAHRS2_GPS0_MASK         (1 << 0) /* Time */
+#define WTGAHRS2_GPS1_MASK         (1 << 1) /* Longitude, Latitude */
+#define WTGAHRS2_GPS2_MASK         (1 << 2) /* Ground speed, Height, Yaw */
+#define WTGAHRS2_GPS_MASK          (7 << 0)
+
+#define WTGAHRS2_GPS0_INFO         0x50
+#define WTGAHRS2_ACCEL_INFO        0x51
+#define WTGAHRS2_GYRO_INFO         0x52
+#define WTGAHRS2_MAG_INFO          0x54
+#define WTGAHRS2_BARO_INFO         0x56
+#define WTGAHRS2_GPS1_INFO         0x57
+#define WTGAHRS2_GPS2_INFO         0x58
+
+#define WTGAHRS2_RSP_HEADER        0x55
+#define WTGAHRS2_RSP_LENGTH        11
+#define WTGAHRS2_CMD_LENGTH        5
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct wtgahrs2_sensor_s
+{
+  struct sensor_lowerhalf_s lower;
+  unsigned int              interval;
+  unsigned int              last_update;
+  bool                      enable;
+};
+
+struct wtgahrs2_dev_s
+{
+  struct wtgahrs2_sensor_s  dev[WTGAHRS2_MAX_IDX];
+  struct file               file;
+
+  struct sensor_event_gps   gps;
+  unsigned char             gps_mask;
+};
+
+/****************************************************************************
+ * Private
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int wtgahrs2_activate(FAR struct sensor_lowerhalf_s *lower, bool sw);
+static int wtgahrs2_set_interval(FAR struct sensor_lowerhalf_s *lower,
+                                 FAR unsigned int *interval);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* in microseconds */
+
+static const unsigned int g_wtgahrs2_interval[] =
+{
+  10000000,  /* 0.1 hz */
+  2000000,   /* 0.5 hz */
+  1000000,   /* 1   hz */
+  500000,    /* 2   hz */
+  200000,    /* 5   hz */
+  100000,    /* 10  hz */
+  50000,     /* 20  hz */
+  20000,     /* 50  hz */
+  10000,     /* 100 hz */
+  5000,      /* 200 hz */
+};
+
+static const uint8_t g_wtgahrs2_unlock[] =
+{
+  0xff, 0xaa, 0x69, 0x88, 0xb5
+};
+
+static const uint8_t g_wtgahrs2_odr_200hz[] =
+{
+  0xff, 0xaa, 0x03, 0x0b, 0x00
+};
+
+static const uint8_t g_wtgahrs2_enable_sensor[] =
+{
+  0xff, 0xaa, 0x02, 0xd7, 0x05
+};
+
+static const struct sensor_ops_s g_wtgahrs2_ops =
+{
+  .activate           = wtgahrs2_activate,
+  .set_interval       = wtgahrs2_set_interval,
+  .batch              = NULL,
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static void wtgahrs2_sendcmd(FAR struct wtgahrs2_dev_s *rtdata,
+                             const void *cmd)
+{
+  file_write(&rtdata->file, cmd, WTGAHRS2_CMD_LENGTH);
+  usleep(10000);
+}
+
+static int wtgahrs2_activate(FAR struct sensor_lowerhalf_s *lower, bool sw)
+{
+  FAR struct wtgahrs2_sensor_s *dev = (FAR struct wtgahrs2_sensor_s *)lower;
+  dev->enable = sw;
+
+  return 0;
+}
+
+static int wtgahrs2_set_interval(FAR struct sensor_lowerhalf_s *lower,
+                                 FAR unsigned int *interval)
+{
+  FAR struct wtgahrs2_sensor_s *dev = (FAR struct wtgahrs2_sensor_s *)lower;
+  int idx = 0;
+
+  for (; idx < WTGAHRS2_ARRAYSIZE(g_wtgahrs2_interval) - 1; idx++)
+    {
+      if (*interval >= g_wtgahrs2_interval[idx])
+        {
+          break;
+        }
+    }
+
+  *interval = g_wtgahrs2_interval[idx];
+  dev->interval = *interval;
+
+  return 0;
+}
+
+static void wtgahrs2_accel_data(FAR struct wtgahrs2_dev_s *rtdata,
+                                FAR unsigned char *buffer)
+{
+  FAR struct wtgahrs2_sensor_s *dev = &rtdata->dev[WTGAHRS2_ACCEL_IDX];
+  FAR struct sensor_lowerhalf_s *lower = &dev->lower;
+  uint64_t now = sensor_get_timestamp();
+  struct sensor_event_accel accel;
+
+  if (!dev->enable || now - dev->last_update < dev->interval)
+    {
+      return;
+    }
+
+  dev->last_update = now;
+
+  accel.timestamp = now;
+  accel.x = (short)(buffer[1] << 8 | buffer[0]) * (16 * 9.8f / 32768);
+  accel.y = (short)(buffer[3] << 8 | buffer[2]) * (16 * 9.8f / 32768);
+  accel.z = (short)(buffer[5] << 8 | buffer[4]) * (16 * 9.8f / 32768);
+  accel.temperature = (short)(buffer[7] << 8 | buffer[6]) / 100.0f;
+
+  lower->push_event(lower->priv, &accel, sizeof(accel));
+  sninfo("Accel: %.3fm/s^2 %.3fm/s^2 %.3fm/s^2, t:%.1f\r\n",
+         accel.x, accel.y, accel.z, accel.temperature);
+}
+
+static void wtgahrs2_gyro_data(FAR struct wtgahrs2_dev_s *rtdata,
+                               FAR unsigned char *buffer)
+{
+  FAR struct wtgahrs2_sensor_s *dev = &rtdata->dev[WTGAHRS2_GYRO_IDX];
+  FAR struct sensor_lowerhalf_s *lower = &dev->lower;
+  uint64_t now = sensor_get_timestamp();
+  struct sensor_event_gyro gyro;
+
+  if (!dev->enable || now - dev->last_update < dev->interval)
+    {
+      return;
+    }
+
+  dev->last_update = now;
+
+  gyro.timestamp = now;
+  gyro.x = (short)(buffer[1] << 8 | buffer[0]) * (2000 * M_PI / 180 / 32768);
+  gyro.y = (short)(buffer[3] << 8 | buffer[2]) * (2000 * M_PI / 180 / 32768);
+  gyro.z = (short)(buffer[5] << 8 | buffer[4]) * (2000 * M_PI / 180 / 32768);
+  gyro.temperature = (short)(buffer[7] << 8 | buffer[6]) / 100.0f;
+
+  lower->push_event(lower->priv, &gyro, sizeof(gyro));
+  sninfo("Gyro: %.3frad/s %.3frad/s %.3frad/s, t:%.1f\r\n",
+          gyro.x, gyro.y, gyro.z, gyro.temperature);
+}
+
+static void wtgahrs2_mag_data(FAR struct wtgahrs2_dev_s *rtdata,
+                              FAR unsigned char *buffer)
+{
+  FAR struct wtgahrs2_sensor_s *dev = &rtdata->dev[WTGAHRS2_MAG_IDX];
+  FAR struct sensor_lowerhalf_s *lower = &dev->lower;
+  uint64_t now = sensor_get_timestamp();
+  struct sensor_event_mag mag;
+
+  if (!dev->enable || now - dev->last_update < dev->interval)
+    {
+      return;
+    }
+
+  dev->last_update = now;
+
+  mag.timestamp = now;
+  mag.x = (short)(buffer[1] << 8 | buffer[0]) * 0.16f;
+  mag.y = (short)(buffer[3] << 8 | buffer[2]) * 0.16f;
+  mag.z = (short)(buffer[5] << 8 | buffer[4]) * 0.16f;
+  mag.temperature = (short)(buffer[7] << 8 | buffer[6]) / 100.0f;
+
+  lower->push_event(lower->priv, &mag, sizeof(mag));
+  sninfo("Mag: %.3fuT %.3fuT %.3fuT, t:%.1f\r\n",
+         mag.x, mag.y, mag.z, mag.temperature);
+}
+
+static void wtgahrs2_baro_data(FAR struct wtgahrs2_dev_s *rtdata,
+                               FAR unsigned char *buffer)
+{
+  FAR struct wtgahrs2_sensor_s *dev = &rtdata->dev[WTGAHRS2_BARO_IDX];
+  FAR struct sensor_lowerhalf_s *lower = &dev->lower;
+  uint64_t now = sensor_get_timestamp();
+  struct sensor_event_baro baro;
+
+  if (!dev->enable || now - dev->last_update < dev->interval)
+    {
+      return;
+    }
+
+  dev->last_update = now;
+
+  baro.timestamp = now;
+  baro.pressure = (long)(buffer[3] << 24 | buffer[2] << 16 |
+                  buffer[1] << 8 | buffer[0]) / 100.0f;
+  baro.temperature = NAN;
+
+  lower->push_event(lower->priv, &baro, sizeof(baro));
+  sninfo("Pressure : %.3fhPa\r\n", baro.pressure);
+}
+
+static void wtgahrs2_gps_data(FAR struct wtgahrs2_dev_s *rtdata,
+                              FAR unsigned char *buffer, int info_type)
+{
+  FAR struct wtgahrs2_sensor_s *dev = &rtdata->dev[WTGAHRS2_GPS_IDX];
+  FAR struct sensor_lowerhalf_s *lower = &dev->lower;
+  uint64_t now = sensor_get_timestamp();
+
+  if (!dev->enable || now - dev->last_update < dev->interval)
+    {
+      return;
+    }
+
+  if (rtdata->gps_mask == 0)
+    {
+      dev->last_update = now;
+    }
+
+  switch (info_type)
+    {
+      case WTGAHRS2_GPS0_INFO:
+        rtdata->gps_mask |= WTGAHRS2_GPS0_MASK;
+        rtdata->gps.year = 2000 + buffer[0];
+        rtdata->gps.month = buffer[1];
+        rtdata->gps.day = buffer[2];
+        rtdata->gps.hour = buffer[3];
+        rtdata->gps.min = buffer[4];
+        rtdata->gps.sec = buffer[5];
+        rtdata->gps.msec = (buffer[7] << 8) | buffer[6];
+        break;
+
+      case WTGAHRS2_GPS1_INFO:
+        rtdata->gps_mask |= WTGAHRS2_GPS1_MASK;
+        rtdata->gps.longitude = (long)(buffer[3] << 8
+                                | buffer[2] << 8
+                                | buffer[1] << 8
+                                | buffer[0]) / 10000000.0f;
+        rtdata->gps.latitude = (long)(buffer[7] << 8
+                               | buffer[6] << 8
+                               | buffer[5] << 8
+                               | buffer[4]) / 10000000.0f;
+        break;
+
+      case WTGAHRS2_GPS2_INFO:
+        rtdata->gps_mask |= WTGAHRS2_GPS2_MASK;
+        rtdata->gps.height = (short)(buffer[1] << 8 | buffer[0]) / 10.0f;
+        rtdata->gps.yaw = (short)(buffer[3] << 8 | buffer[2]) / 10.0f;
+        rtdata->gps.speed = (long)(buffer[7] << 8 | buffer[6] << 8
+                    | buffer[5] << 8 | buffer[4]) / 3600.0f;
+        break;
+    }
+
+  if (rtdata->gps_mask == WTGAHRS2_GPS_MASK)
+    {
+      rtdata->gps_mask = 0;
+      lower->push_event(lower->priv, &rtdata->gps, sizeof(rtdata->gps));
+      sninfo("Time : %d/%d/%d-%d:%d:%d\r\n", rtdata->gps.year,
+              rtdata->gps.month, rtdata->gps.day, rtdata->gps.hour,
+              rtdata->gps.min, rtdata->gps.sec);
+      sninfo("GPS longitude : %fdegree, latitude:%fdegree\r\n",
+              rtdata->gps.longitude, rtdata->gps.latitude);
+      sninfo("GPS speed: %fm/s, yaw:%fdegrees, height:%fm \r\n",
+              rtdata->gps.speed, rtdata->gps.yaw, rtdata->gps.height);
+    }
+}
+
+static bool wtgahrs2_process_data(FAR struct wtgahrs2_dev_s *rtdata,
+                                  FAR unsigned char *buffer)
+{
+  unsigned char sum = 0;
+  int i;
+
+  /* calculate sum and verify checksum */
+
+  for (i = 0; i < WTGAHRS2_RSP_LENGTH - 1; i++)
+    {
+      sum += buffer[i];
+    }
+
+  if (sum != buffer[WTGAHRS2_RSP_LENGTH - 1])
+    {
+      return false;
+    }
+
+  switch (buffer[1])
+    {
+      case WTGAHRS2_ACCEL_INFO:
+        wtgahrs2_accel_data(rtdata, &buffer[2]);
+        break;
+
+      case WTGAHRS2_GYRO_INFO:
+        wtgahrs2_gyro_data(rtdata, &buffer[2]);
+        break;
+
+      case WTGAHRS2_MAG_INFO:
+        wtgahrs2_mag_data(rtdata, &buffer[2]);
+        break;
+
+      case WTGAHRS2_BARO_INFO:
+        wtgahrs2_baro_data(rtdata, &buffer[2]);
+        break;
+
+      case WTGAHRS2_GPS0_INFO:
+      case WTGAHRS2_GPS1_INFO:
+      case WTGAHRS2_GPS2_INFO:
+        wtgahrs2_gps_data(rtdata, &buffer[2], buffer[1]);
+        break;
+    }
+
+  return true;
+}
+
+static int wtgahrs2_thread(int argc, FAR char *argv[])
+{
+  FAR struct wtgahrs2_dev_s *rtdata = (FAR struct wtgahrs2_dev_s *)
+                                      ((uintptr_t)strtoul(argv[1], NULL, 0));
+  unsigned char buffer[8 * WTGAHRS2_RSP_LENGTH];
+  ssize_t count = 0;
+  ssize_t pos;
+
+  while (1)
+    {
+      count += file_read(&rtdata->file, buffer + count,
+                         sizeof(buffer) - count);
+      for (pos = 0; pos < count; pos++)
+        {
+          if (buffer[pos] != WTGAHRS2_RSP_HEADER)
+            {
+              continue;
+            }
+
+          if (count - pos < WTGAHRS2_RSP_LENGTH)
+            {
+              memmove(buffer, buffer + pos, count - pos);
+              break;
+            }
+
+          if (wtgahrs2_process_data(rtdata, &buffer[pos]))
+            {
+              pos += WTGAHRS2_RSP_LENGTH - 1;
+            }
+        }
+
+        count -= pos;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int wtgahrs2_initialize(FAR const char *path)
+{
+  FAR struct wtgahrs2_dev_s *rtdata;
+  FAR struct wtgahrs2_sensor_s *tmp;
+#if CONFIG_SERIAL_TERMIOS
+  struct termios opt;
+#endif
+  FAR char *argv[2];
+  char arg1[16];
+  int ret;
+
+  if (!path)
+    {
+      snerr("Invaild path for serial interface\n");
+      return -EINVAL;
+    }
+
+  rtdata = kmm_zalloc(sizeof(struct wtgahrs2_dev_s));
+  if (!rtdata)
+    {
+      snerr("Memory cannot be allocated for wtgahrs2\n");
+      return -ENOMEM;
+    }
+
+  /* Open serial tty port and set baud rate */
+
+  ret = file_open(&rtdata->file, path, O_RDWR);
+  if (ret < 0)
+    {
+      snerr("Failed to open wtgahrs2 serial:%s, err:%d", path, ret);
+      goto open_err;
+    }
+
+#if CONFIG_SERIAL_TERMIOS
+  file_ioctl(&rtdata->file, TCGETS, &opt);
+  cfmakeraw(&opt);
+  cfsetispeed(&opt, B115200);
+  cfsetospeed(&opt, B115200);
+  file_ioctl(&rtdata->file, TCSETS, &opt);
+#endif
+
+  /* Accelerometer register */
+
+  tmp = &rtdata->dev[WTGAHRS2_ACCEL_IDX];
+  tmp->lower.ops = &g_wtgahrs2_ops;
+  tmp->lower.type = SENSOR_TYPE_ACCELEROMETER;
+  tmp->lower.buffer_bytes = sizeof(struct sensor_event_accel);
+  ret = sensor_register(&tmp->lower);
+  if (ret < 0)
+    {
+      goto accel_err;
+    }
+
+  /* Gyroscope register */
+
+  tmp = &rtdata->dev[WTGAHRS2_GYRO_IDX];
+  tmp->lower.ops = &g_wtgahrs2_ops;
+  tmp->lower.type = SENSOR_TYPE_GYROSCOPE;
+  tmp->lower.buffer_bytes = sizeof(struct sensor_event_gyro);
+  ret = sensor_register(&tmp->lower);
+  if (ret < 0)
+    {
+      goto gyro_err;
+    }
+
+  /* Magnetic register */
+
+  tmp = &rtdata->dev[WTGAHRS2_MAG_IDX];
+  tmp->lower.ops = &g_wtgahrs2_ops;
+  tmp->lower.type = SENSOR_TYPE_MAGNETIC_FIELD;
+  tmp->lower.buffer_bytes = sizeof(struct sensor_event_mag);
+  ret = sensor_register(&tmp->lower);
+  if (ret < 0)
+    {
+      goto mag_err;
+    }
+
+  /* Barometer register */
+
+  tmp = &rtdata->dev[WTGAHRS2_BARO_IDX];
+  tmp->lower.ops = &g_wtgahrs2_ops;
+  tmp->lower.type = SENSOR_TYPE_BAROMETER;
+  tmp->lower.buffer_bytes = sizeof(struct sensor_event_baro);
+  ret = sensor_register(&tmp->lower);
+  if (ret < 0)
+    {
+      goto baro_err;
+    }
+
+  /* GPS register */
+
+  tmp = &rtdata->dev[WTGAHRS2_GPS_IDX];
+  tmp->lower.ops = &g_wtgahrs2_ops;
+  tmp->lower.type = SENSOR_TYPE_GPS;
+  tmp->lower.buffer_bytes = sizeof(struct sensor_event_gps);
+  ret = sensor_register(&tmp->lower);
+  if (ret < 0)
+    {
+      goto gps_err;
+    }
+
+  /* Set sensor default attributes and enter unlock mode */
+
+  wtgahrs2_sendcmd(rtdata, g_wtgahrs2_unlock);
+
+  /* Set sensor default odr 200hz */
+
+  wtgahrs2_sendcmd(rtdata, g_wtgahrs2_odr_200hz);
+
+  /* Enable all sensor */
+
+  wtgahrs2_sendcmd(rtdata, g_wtgahrs2_enable_sensor);
+
+  snprintf(arg1, 16, "0x%" PRIxPTR, (uintptr_t)rtdata);
+  argv[0] = arg1;
+  argv[1] = NULL;
+
+  ret = kthread_create("wtgahrs2_thread", SCHED_PRIORITY_DEFAULT,
+                       CONFIG_DEFAULT_TASK_STACKSIZE,
+                       wtgahrs2_thread, argv);
+  if (ret < 0)
+    {
+      goto thr_err;
+    }
+
+  return ret;
+
+thr_err:
+  sensor_unregister(&rtdata->dev[WTGAHRS2_GPS_IDX].lower);
+gps_err:
+  sensor_unregister(&rtdata->dev[WTGAHRS2_BARO_IDX].lower);
+baro_err:
+  sensor_unregister(&rtdata->dev[WTGAHRS2_MAG_IDX].lower);
+mag_err:
+  sensor_unregister(&rtdata->dev[WTGAHRS2_GYRO_IDX].lower);
+gyro_err:
+  sensor_unregister(&rtdata->dev[WTGAHRS2_ACCEL_IDX].lower);
+accel_err:
+  file_close(&rtdata->file);
+open_err:
+  kmm_free(rtdata);
+  return ret;
+}
diff --git a/include/nuttx/sensors/wtgahrs2.h b/include/nuttx/sensors/wtgahrs2.h
new file mode 100644
index 0000000..7d86254
--- /dev/null
+++ b/include/nuttx/sensors/wtgahrs2.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+ * include/nuttx/sensors/wtgahrs2.h
+ * Driver for the Wit-Motion WTGAHRS2 accelerometer, gyroscope, magnetic,
+ * angle, barometer, temperature, gps sensors by serial interface with host
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_NUTTX_SENSORS_WTGAHRS2_H
+#define __INCLUDE_NUTTX_SENSORS_WTGAHRS2_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/sensors/sensor.h>
+#include <nuttx/sensors/ioctl.h>
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/****************************************************************************
+ * Name: wtgahrs2_initialize
+ *
+ * Description:
+ *   Initialize wrgahrs2 sensor module, it will create accelerometer,
+ *   gyroscope, magnetic, barometer, gps character device.
+ *
+ * Input Parameters:
+ *   devpath - The full path to the driver to read data source by serial tty.
+ *
+ * Returned Value:
+ *   OK if the driver was successfully initialize; A negated errno value is
+ *   returned on any failure.
+ ****************************************************************************/
+
+int wtgahrs2_initialize(FAR const char *path);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif