diff --git a/src/commandpanel.cpp b/src/commandpanel.cpp
--- a/src/commandpanel.cpp
+++ b/src/commandpanel.cpp
@@ -1,5 +1,5 @@
 /*
-  Copyright © 2015 Hasan Yavuz Özderya
+  Copyright © 2016 Hasan Yavuz Özderya
 
   This file is part of serialplot.
 
@@ -17,11 +17,12 @@
   along with serialplot.  If not, see .
 */
 
+#include 
+#include 
+
 #include "commandpanel.h"
 #include "ui_commandpanel.h"
-
-#include 
-#include 
+#include "setting_defines.h"
 
 CommandPanel::CommandPanel(QSerialPort* port, QWidget *parent) :
     QWidget(parent),
@@ -46,15 +47,15 @@ CommandPanel::CommandPanel(QSerialPort* 
     _menu.addSeparator();
 
     command_name_counter = 0;
-    newCommand(); // add an empty slot by default
 }
 
 CommandPanel::~CommandPanel()
 {
+    commands.clear(); // UI will 'delete' actual objects
     delete ui;
 }
 
-void CommandPanel::newCommand()
+CommandWidget* CommandPanel::newCommand()
 {
     auto command = new CommandWidget();
     command_name_counter++;
@@ -64,6 +65,14 @@ void CommandPanel::newCommand()
     connect(command, &CommandWidget::sendCommand, this, &CommandPanel::sendCommand);
     connect(command, &CommandWidget::focusRequested, this, &CommandPanel::focusRequested);
     _menu.addAction(command->sendAction());
+
+    // add to command list and remove on destroy
+    commands << command;
+    connect(command, &QObject::destroyed, [this](QObject* obj)
+            {
+                commands.removeOne(static_cast(obj));
+            });
+    return command;
 }
 
 void CommandPanel::sendCommand(QByteArray command)
@@ -89,3 +98,66 @@ QAction* CommandPanel::newCommandAction(
 {
     return &_newCommandAction;
 }
+
+unsigned CommandPanel::numOfCommands()
+{
+    return commands.size();
+}
+
+void CommandPanel::saveSettings(QSettings* settings)
+{
+    settings->beginGroup(SettingGroup_Commands);
+    settings->beginWriteArray(SG_Commands_Command);
+    for (int i = 0; i < commands.size(); i ++)
+    {
+        settings->setArrayIndex(i);
+        auto command = commands[i];
+        settings->setValue(SG_Commands_Name, command->name());
+        settings->setValue(SG_Commands_Type, command->isASCIIMode() ? "ascii" : "hex");
+        settings->setValue(SG_Commands_Data, command->commandText());
+    }
+    settings->endArray();
+    settings->endGroup();
+}
+
+void CommandPanel::loadSettings(QSettings* settings)
+{
+    // clear all commands
+    while (commands.size())
+    {
+        auto command = commands.takeLast();
+        command->disconnect();
+        delete command;
+    }
+
+    // load commands
+    settings->beginGroup(SettingGroup_Commands);
+    unsigned size = settings->beginReadArray(SG_Commands_Command);
+
+    for (unsigned i = 0; i < size; i ++)
+    {
+        settings->setArrayIndex(i);
+        auto command = newCommand();
+
+        // load command name
+        QString name = settings->value(SG_Commands_Name, "").toString();
+        if (!name.isEmpty()) command->setName(name);
+
+        // Important: type should be set before command data for correct validation
+        QString type = settings->value(SG_Commands_Type, "").toString();
+        if (type == "ascii")
+        {
+            command->setASCIIMode(true);
+        }
+        else if (type == "hex")
+        {
+            command->setASCIIMode(false);
+        } // else unchanged
+
+        // load command data
+        command->setCommandText(settings->value(SG_Commands_Data, "").toString());
+    }
+
+    settings->endArray();
+    settings->endGroup();
+}