diff --git a/Tools/px4moduledoc/markdownout.py b/Tools/px4moduledoc/markdownout.py
index ce5e2b52c09123ebbc9918ed310ed0a292dfc068..ba59f82e19a4e3b869940ad753f10accc0c4ba27 100644
--- a/Tools/px4moduledoc/markdownout.py
+++ b/Tools/px4moduledoc/markdownout.py
@@ -40,22 +40,40 @@ The generated files will be written to the `modules` directory.
 
         self._outputs['main'] = result
 
-
         for category in sorted(module_groups):
             result = "# Modules Reference: %s\n" % category.capitalize()
-            module_list = module_groups[category]
-            for module in module_list:
-                result += "## %s\n" % module.name()
-                result += "Source: [%s](https://github.com/PX4/Firmware/tree/master/src/%s)\n\n" % (module.scope(), module.scope())
-                doc = module.documentation()
-                if len(doc) > 0:
-                    result += "%s\n" % doc
-                usage_string = module.usage_string()
-                if len(usage_string) > 0:
-                    result += "### Usage {#%s_usage}\n```\n%s\n```\n" % (module.name(), usage_string)
+            subcategories = module_groups[category]
+            if len(subcategories) > 1:
+                result += 'Subcategories:\n'
+                for subcategory in subcategories:
+                    if subcategory == '':
+                        continue
+                    subcategory_label = subcategory.replace('_', ' ').title()
+                    subcategory_file_name = category+'_'+subcategory
+                    result += '- [%s](modules_%s.md)\n' % (subcategory_label, subcategory_file_name)
+
+                    # add a sub-page for the subcategory
+                    result_subpage = '# Modules Reference: %s (%s)\n' % \
+                        (subcategory_label, category.capitalize())
+                    result_subpage += self._ProcessModules(subcategories[subcategory])
+                    self._outputs[subcategory_file_name] = result_subpage
 
+            result += '\n' + self._ProcessModules(subcategories[''])
             self._outputs[category] = result
 
+    def _ProcessModules(self, module_list):
+        result = ''
+        for module in module_list:
+            result += "## %s\n" % module.name()
+            result += "Source: [%s](https://github.com/PX4/Firmware/tree/master/src/%s)\n\n" % (module.scope(), module.scope())
+            doc = module.documentation()
+            if len(doc) > 0:
+                result += "%s\n" % doc
+            usage_string = module.usage_string()
+            if len(usage_string) > 0:
+                result += "### Usage {#%s_usage}\n```\n%s\n```\n" % (module.name(), usage_string)
+        return result
+
     def Save(self, dirname):
         for output_name in self._outputs:
             output = self._outputs[output_name]
diff --git a/Tools/px4moduledoc/srcparser.py b/Tools/px4moduledoc/srcparser.py
index 2e227699e66b5f86bc79a873f85b5677620258f7..2c29645fa332f98b51b29717884890093229ce1e 100644
--- a/Tools/px4moduledoc/srcparser.py
+++ b/Tools/px4moduledoc/srcparser.py
@@ -8,8 +8,11 @@ class ModuleDocumentation(object):
     documentation for a single module
     """
 
+    # If you add categories or subcategories, they also need to be added to the
+    # TOC in https://github.com/PX4/Devguide/blob/master/en/SUMMARY.md
     valid_categories = ['driver', 'estimator', 'controller', 'system',
                         'communication', 'command', 'template']
+    valid_subcategories = ['', 'distance_sensor']
 
     max_line_length = 80 # wrap lines that are longer than this
 
@@ -19,6 +22,7 @@ class ModuleDocumentation(object):
         """
         self._name = ''
         self._category = ''
+        self._subcategory = ''
         self._doc_string = ''
         self._usage_string = ''
         self._first_command = True
@@ -43,7 +47,6 @@ class ModuleDocumentation(object):
         assert(len(args) == 1) # description
         self._doc_string = self._get_string(args[0])
 
-
     def _handle_usage_name(self, args):
         assert(len(args) == 2) # executable_name, category
         self._name = self._get_string(args[0])
@@ -52,6 +55,10 @@ class ModuleDocumentation(object):
         self._usage_string = "%s <command> [arguments...]\n" % self._name
         self._usage_string += " Commands:\n"
 
+    def _handle_usage_subcategory(self, args):
+        assert(len(args) == 1) # description
+        self._subcategory = self._get_string(args[0])
+
     def _handle_usage_name_simple(self, args):
         assert(len(args) == 2) # executable_name, category
         self._name = self._get_string(args[0])
@@ -196,6 +203,9 @@ class ModuleDocumentation(object):
     def category(self):
         return self._category
 
+    def subcategory(self):
+        return self._subcategory
+
     def scope(self):
         return self._scope
 
@@ -304,6 +314,9 @@ class SourceParser(object):
             if not module_doc.category() in ModuleDocumentation.valid_categories:
                 raise  Exception('Invalid/unknown category ' +
                         module_doc.category() + ' for ' + scope)
+            if not module_doc.subcategory() in ModuleDocumentation.valid_subcategories:
+                raise  Exception('Invalid/unknown subcategory ' +
+                        module_doc.subcategory() + ' for ' + scope)
 
             self._do_consistency_check(contents, scope, module_doc)
 
@@ -483,19 +496,25 @@ class SourceParser(object):
 
     def GetModuleGroups(self):
         """
-        Returns a dictionary of all categories with a list of associated modules.
+        Returns a dictionary of all categories with a dictonary of subcategories
+        that contain a list of associated modules.
         """
         groups = {}
         for module_name in self._modules:
             module = self._modules[module_name]
+            subcategory = module.subcategory()
             if module.category() in groups:
-                groups[module.category()].append(module)
+                if subcategory in groups[module.category()]:
+                    groups[module.category()][subcategory].append(module)
+                else:
+                    groups[module.category()][subcategory] = [module]
             else:
-                groups[module.category()]= [module]
+                groups[module.category()] = {subcategory: [module]}
 
         # sort by module name
         for category in groups:
             group = groups[category]
-            groups[category] = sorted(group, key=lambda x: x.name())
+            for subcategory in group:
+                group[subcategory] = sorted(group[subcategory], key=lambda x: x.name())
         return groups
 
diff --git a/Tools/px_process_module_doc.py b/Tools/px_process_module_doc.py
index 569dc0cad72ce02ede2bc3643ee8097844c43a05..b09ec606c3caddc94aea07bcb5d08583c0d87180 100755
--- a/Tools/px_process_module_doc.py
+++ b/Tools/px_process_module_doc.py
@@ -93,7 +93,7 @@ def main():
 
     # Output to Markdown/HTML tables
     if args.markdown:
-        if (args.verbose): print("Creating markdown output to directory " + str(args.markdown))
+        if args.verbose: print("Creating markdown output to directory " + str(args.markdown))
         if not os.path.exists(args.markdown):
             os.makedirs(args.markdown)
         out = markdownout.MarkdownOutput(module_groups)
diff --git a/src/drivers/distance_sensor/sf1xx/sf1xx.cpp b/src/drivers/distance_sensor/sf1xx/sf1xx.cpp
index 2d5ae434461f27158077523f73dc397fd2f2e8b6..ba67d56fae19c7531d9a7c4f06d71e5f4e7a332b 100644
--- a/src/drivers/distance_sensor/sf1xx/sf1xx.cpp
+++ b/src/drivers/distance_sensor/sf1xx/sf1xx.cpp
@@ -835,6 +835,7 @@ $ sf1xx stop
 )DESCR_STR");
 
 	PRINT_MODULE_USAGE_NAME("sf1xx", "driver");
+	PRINT_MODULE_USAGE_SUBCATEGORY("distance_sensor");
 	PRINT_MODULE_USAGE_COMMAND_DESCR("start","Start driver");
 	PRINT_MODULE_USAGE_PARAM_FLAG('a', "Attempt to start driver on all I2C buses", true);
 	PRINT_MODULE_USAGE_PARAM_INT('b', 1, 1, 2000, "Start driver on specific I2C bus", true);
diff --git a/src/platforms/common/module.cpp b/src/platforms/common/module.cpp
index 31e8b6c7af10867075b99b4048712e8e9bd82994..319fc6b583523156119c4a624bcca0230b22bd0a 100644
--- a/src/platforms/common/module.cpp
+++ b/src/platforms/common/module.cpp
@@ -64,6 +64,11 @@ void PRINT_MODULE_USAGE_NAME(const char *executable_name, const char *category)
 	PX4_INFO_RAW(" Commands:\n");
 }
 
+void PRINT_MODULE_USAGE_SUBCATEGORY(const char *subcategory)
+{
+	(void)subcategory;
+}
+
 void PRINT_MODULE_USAGE_NAME_SIMPLE(const char *executable_name, const char *category)
 {
 	PX4_INFO_RAW("Usage: %s [arguments...]\n", executable_name);
diff --git a/src/platforms/px4_module.h b/src/platforms/px4_module.h
index c3806da11019b2959bac5a8f03b4348065994aba..150da68c24aad167bec252f7788b7e44ac0ce126 100644
--- a/src/platforms/px4_module.h
+++ b/src/platforms/px4_module.h
@@ -472,6 +472,12 @@ __EXPORT void PRINT_MODULE_DESCRIPTION(const char *description);
  */
 __EXPORT void PRINT_MODULE_USAGE_NAME(const char *executable_name, const char *category);
 
+/**
+ * @brief Specify a subcategory.
+ * @param subcategory e.g. if the category is 'driver', subcategory can be 'distance_sensor'
+ */
+__EXPORT void PRINT_MODULE_USAGE_SUBCATEGORY(const char *subcategory);
+
 /**
  * @brief Prints the name for a command without any sub-commands (@see PRINT_MODULE_USAGE_NAME()).
  */