Data acquisition and device control with Scilab.

Dependencies:   Servo MPU6050

Desciription

Scilab is a freeware alternative to MATLAB. For low-cost data acquisition and device control a nice Arduino toolbox is available.

https://os.mbed.com/media/uploads/hudakz/scilab.png

This site presents a Mbed port which allows to use Mbed boards (equipped with Arduino header) rather than Arduino to import real time data into Scilab and to control real equipment witch Scilab.

https://os.mbed.com/media/uploads/hudakz/arduino-temp-read_imagelarge_1.jpeg

Installation
  • Install Scilab to your PC, if not done yet.
  • Launch Scilab and install the Arduino toolbox by executing the following command from the Scilab console:

--> atomsInstall("arduino")
Controlling Mbed's digital output from Scilab
  • In Xcos open examples/Arduino1.zcos

/media/uploads/hudakz/scilab_arduino1.png

  • Double click on the Board setup block and replace the serial port number with mbed's actual virtual serial port number.
  • Double click on the Digital WRITE block and set Digital Pin to 13 (D13 is connected to LED1).
  • Start simulation and LED1 on the Mbed board should start blinking.
Reading and displaying Mbed's analog input
  • In Xcos open examples/Arduino2.zcos

/media/uploads/hudakz/scilab_arduino2.png

  • Double click on the Board setup block and replace the serial port number with mbed's actual virtual serial port number.
  • Double click on the Analog READ block and set Analog Pin to 2.
  • Start simulation and a graph should appear showing the analog signal measured on Mbed's pin A2.

NOTE: Currently, there is bug in the toolbox ARDUINO_ANALOG_READ_sim function (I have reported to Scilab) so the analog readings are not correct.

/media/uploads/hudakz/scilab_graph.png

PID controller
  • In Xcos open examples/Arduino9.zcos

/media/uploads/hudakz/scilab_arduino9.png

Files at this revision

API Documentation at this revision

Comitter:
hudakz
Date:
Mon Jan 18 19:51:22 2021 +0000
Commit message:
Data acquisition and device control with Scilab.

Changed in this revision

.gitignore Show annotated file Show diff for this revision Revisions of this file
CONTRIBUTING.md Show annotated file Show diff for this revision Revisions of this file
MPU6050.lib Show annotated file Show diff for this revision Revisions of this file
README.md Show annotated file Show diff for this revision Revisions of this file
ScilabSerial/DcMotor.cpp Show annotated file Show diff for this revision Revisions of this file
ScilabSerial/DcMotor.h Show annotated file Show diff for this revision Revisions of this file
ScilabSerial/Encoder.cpp Show annotated file Show diff for this revision Revisions of this file
ScilabSerial/Encoder.h Show annotated file Show diff for this revision Revisions of this file
ScilabSerial/IntrInCounter.cpp Show annotated file Show diff for this revision Revisions of this file
ScilabSerial/IntrInCounter.h Show annotated file Show diff for this revision Revisions of this file
ScilabSerial/ScilabSerial.cpp Show annotated file Show diff for this revision Revisions of this file
ScilabSerial/ScilabSerial.h Show annotated file Show diff for this revision Revisions of this file
Servo.lib Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed-os.lib Show annotated file Show diff for this revision Revisions of this file
resources/official_armmbed_example_badge.png Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.gitignore	Mon Jan 18 19:51:22 2021 +0000
@@ -0,0 +1,4 @@
+.build
+.mbed
+projectfiles
+*.py*
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CONTRIBUTING.md	Mon Jan 18 19:51:22 2021 +0000
@@ -0,0 +1,5 @@
+# Contributing to Mbed OS
+
+Mbed OS is an open-source, device software platform for the Internet of Things. Contributions are an important part of the platform, and our goal is to make it as simple as possible to become a contributor.
+
+To encourage productive collaboration, as well as robust, consistent and maintainable code, we have a set of guidelines for [contributing to Mbed OS](https://os.mbed.com/docs/mbed-os/latest/contributing/index.html).
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MPU6050.lib	Mon Jan 18 19:51:22 2021 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/hudakz/code/MPU6050/#9c2bb0f94c31
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README.md	Mon Jan 18 19:51:22 2021 +0000
@@ -0,0 +1,64 @@
+![](./resources/official_armmbed_example_badge.png)
+# Blinky Mbed OS example
+
+The example project is part of the [Arm Mbed OS Official Examples](https://os.mbed.com/code/) and is the [getting started example for Mbed OS](https://os.mbed.com/docs/mbed-os/v5.14/quick-start/index.html). It contains an application that repeatedly blinks an LED on supported [Mbed boards](https://os.mbed.com/platforms/).
+
+You can build the project with all supported [Mbed OS build tools](https://os.mbed.com/docs/mbed-os/latest/tools/index.html). However, this example project specifically refers to the command-line interface tool [Arm Mbed CLI](https://github.com/ARMmbed/mbed-cli#installing-mbed-cli).
+(Note: To see a rendered example you can import into the Arm Online Compiler, please see our [import quick start](https://os.mbed.com/docs/mbed-os/latest/quick-start/online-with-the-online-compiler.html#importing-the-code).)
+
+1. [Install Mbed CLI](https://os.mbed.com/docs/mbed-os/latest/quick-start/offline-with-mbed-cli.html).
+
+1. Clone this repository on your system, and change the current directory to where the project was cloned:
+
+    ```bash
+    $ git clone git@github.com:armmbed/mbed-os-example-blinky && cd mbed-os-example-blinky
+    ```
+
+    Alternatively, you can download the example project with Arm Mbed CLI using the `import` subcommand:
+
+    ```bash
+    $ mbed import mbed-os-example-blinky && cd mbed-os-example-blinky
+    ```
+
+
+## Application functionality
+
+The `main()` function is the single thread in the application. It toggles the state of a digital output connected to an LED on the board.
+
+## Building and running
+
+1. Connect a USB cable between the USB port on the board and the host computer.
+2. <a name="build_cmd"></a> Run the following command to build the example project and program the microcontroller flash memory:
+    ```bash
+    $ mbed compile -m <TARGET> -t <TOOLCHAIN> --flash
+    ```
+The binary is located at `./BUILD/<TARGET>/<TOOLCHAIN>/mbed-os-example-blinky.bin`.
+
+Alternatively, you can manually copy the binary to the board, which you mount on the host computer over USB.
+
+Depending on the target, you can build the example project with the `GCC_ARM`, `ARM` or `IAR` toolchain. After installing Arm Mbed CLI, run the command below to determine which toolchain supports your target:
+
+```bash
+$ mbed compile -S
+```
+
+## Expected output
+The LED on your target turns on and off every 500 milliseconds.
+
+
+## Troubleshooting
+If you have problems, you can review the [documentation](https://os.mbed.com/docs/latest/tutorials/debugging.html) for suggestions on what could be wrong and how to fix it.
+
+## Related Links
+
+* [Mbed OS Stats API](https://os.mbed.com/docs/latest/apis/mbed-statistics.html).
+* [Mbed OS Configuration](https://os.mbed.com/docs/latest/reference/configuration.html).
+* [Mbed OS Serial Communication](https://os.mbed.com/docs/latest/tutorials/serial-communication.html).
+* [Mbed OS bare metal](https://os.mbed.com/docs/mbed-os/latest/reference/mbed-os-bare-metal.html).
+* [Mbed boards](https://os.mbed.com/platforms/).
+
+### License and contributions
+
+The software is provided under Apache-2.0 license. Contributions to this project are accepted under the same license. Please see contributing.md for more info.
+
+This project contains code from other projects. The original license text is included in those source files. They must comply with our license guide.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ScilabSerial/DcMotor.cpp	Mon Jan 18 19:51:22 2021 +0000
@@ -0,0 +1,66 @@
+#include "DcMotor.h"
+
+/**
+ * @brief   The DcMotor class
+ * @note    Driven by L293 or L298
+ */
+DcMotor::DcMotor(PinName pin1, PinName pin2, int driverType) :
+    _driverType(driverType)
+{
+    _pwmOut1 = new PwmOut(pin1);
+    _pwmOut1->write(0);
+    switch (_driverType) {
+        case 0: //L293
+            _pwmOut2 = new PwmOut(pin2);
+            _pwmOut2->write(0);
+            break;
+
+        case 1: //L297
+            _digitalOut2 = new DigitalOut(pin2);
+            _digitalOut2->write(0);
+            break;
+    }
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+DcMotor::~DcMotor()
+{
+    delete _pwmOut1;
+    delete _pwmOut2;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void DcMotor::run(int direction, float speed)
+{
+    switch (_driverType) {
+        case 0: //L293
+            if (direction == 0) {
+                _pwmOut1->write(0);
+                _pwmOut2->write(speed);
+            }
+            else {
+                _pwmOut1->write(speed);
+                _pwmOut2->write(0);
+            }
+            break;
+
+        case 1: //L297
+            if (direction == 0)
+                _digitalOut2->write(0);
+            else
+                _digitalOut2->write(1);
+
+            _pwmOut1->write(speed);
+            break;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ScilabSerial/DcMotor.h	Mon Jan 18 19:51:22 2021 +0000
@@ -0,0 +1,18 @@
+#ifndef DCMOTOR_H
+#define DCMOTOR_H
+
+#include "mbed.h"
+
+class DcMotor
+{
+    PwmOut*         _pwmOut1;
+    PwmOut*         _pwmOut2;
+    DigitalOut*     _digitalOut2;
+    int             _driverType;
+public:
+    DcMotor(PinName pin1, PinName pin2, int driverType);
+    ~DcMotor();
+    void run(int direction, float speed);
+};
+
+#endif // DCMOTOR_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ScilabSerial/Encoder.cpp	Mon Jan 18 19:51:22 2021 +0000
@@ -0,0 +1,65 @@
+#include "Encoder.h"
+
+/**
+ * @brief   The Encoder class
+ * @note    Rotary encoder with three operating modes
+ */
+Encoder::Encoder(PinName pinA, PinName pinB, int mode) : _position(0)
+{
+    switch (mode) {
+        case 1:
+            _chanA = new InterruptIn(pinA);
+            _chanA->rise(callback(this, &Encoder::onRiseMode1));
+            _chanB = new InterruptIn(pinB);
+            break;
+
+        case 2:
+            _chanA = new InterruptIn(pinA);
+            _chanA->rise(callback(this, &Encoder::onChangeMode2));
+            _chanA->fall(callback(this, &Encoder::onChangeMode2));
+            _chanB = new InterruptIn(pinB);
+            break;
+
+        case 4:
+            _chanA = new InterruptIn(pinA);
+            _chanA->rise(callback(this, &Encoder::onChangeMode2));
+            _chanA->fall(callback(this, &Encoder::onChangeMode2));
+            _chanB = new InterruptIn(pinB);
+            _chanB->rise(callback(this, &Encoder::onChangeMode2));
+            _chanB->fall(callback(this, &Encoder::onChangeMode2));
+            break;
+    }
+}
+
+Encoder::~Encoder()
+{
+    delete _chanA;
+    delete _chanB;
+}
+
+void Encoder::onRiseMode1()
+{
+    int b = _chanB->read();
+    if (b)
+        _position--;
+    else
+        _position++;
+}
+void Encoder::onChangeMode2()
+{
+    int a = _chanA->read();
+    int b = _chanB->read();
+    if ((a && !b) || (!a && b))
+        _position++;
+    else
+        _position--;
+}
+void Encoder::onChangeMode4()
+{
+    int a = _chanA->read();
+    int b = _chanB->read();
+    if ((a && !b) || (!a && b))
+        _position++;
+    else
+        _position--;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ScilabSerial/Encoder.h	Mon Jan 18 19:51:22 2021 +0000
@@ -0,0 +1,20 @@
+#ifndef ENCODER_H
+#define ENCODER_H
+
+#include "mbed.h"
+
+class   Encoder
+{
+    InterruptIn*    _chanA;
+    InterruptIn*    _chanB;
+    int8_t          _position;
+public:
+    Encoder(PinName pinA, PinName pinB, int mode);
+    ~Encoder();
+    int8_t  position()  { return _position; }
+    void    reset()     { _position = 0; }
+    void    onRiseMode1();
+    void    onChangeMode2();
+    void    onChangeMode4();
+};
+#endif // ENCODER_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ScilabSerial/IntrInCounter.cpp	Mon Jan 18 19:51:22 2021 +0000
@@ -0,0 +1,6 @@
+#include "IntrInCounter.h"
+
+IntrInCounter::IntrInCounter(PinName pin) : _value(0), _interruptIn(new InterruptIn(pin))
+{
+    _interruptIn->rise(callback(this, &IntrInCounter::inc));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ScilabSerial/IntrInCounter.h	Mon Jan 18 19:51:22 2021 +0000
@@ -0,0 +1,21 @@
+#ifndef INTRINCOUNTER_H
+#define INTRINCOUNTER_H
+
+#include "mbed.h"
+
+/**
+ * @brief   The IntInCounter class
+ * @note    Incremented on rising edge of input signal connected to InterruptIn pin
+ */
+class   IntrInCounter
+{
+    uint8_t         _value;
+    InterruptIn*    _interruptIn;
+public:
+    IntrInCounter(PinName pin);
+    void    inc()                   { _value++; }
+    void    reset()                 { _value = 0; }
+    uint8_t read()                  { return _value; }
+    void    activate(PinName pin)   { }
+};
+#endif // INTINCOUNTER_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ScilabSerial/ScilabSerial.cpp	Mon Jan 18 19:51:22 2021 +0000
@@ -0,0 +1,583 @@
+#include "ScilabSerial.h"
+
+extern UnbufferedSerial dbgPort;
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+ScilabSerial::ScilabSerial(PinName tx, PinName rx, int baud /*= 115200*/ ) :
+    SerialBase(tx, rx, baud),
+    _rxComplete(false),
+    _rxBufLen(0),
+    _rxDataLen(0),
+    _deltaT(0),
+    _mpu6050Available(false)
+{
+    attach(callback(this, &ScilabSerial::onRx));
+
+    // Analog inputs
+    for (size_t i = 0; i < sizeof(_analogPinName) / sizeof(*_analogPinName); i++) {
+        // indexes -> pin names
+        if (_analogPinName[i] != NC)
+            _analogIns[i] = new AnalogIn(_analogPinName[i]);
+        else
+            _analogIns[i] = NULL;
+    }
+
+    // PWM outputs
+    for (size_t i = 0; i < 4; i++) {
+        // indexes 0 to 3 -> pin names D4 to D7
+        if (_digitalPinName[i + 4] != NC)
+            _pwmOuts[i] = new PwmOut(_digitalPinName[i + 4]);
+        else
+            _pwmOuts[i] = NULL;
+    }
+
+    // Digital read write
+    for (size_t i = 0; i < sizeof(_digitalInOuts) / sizeof(*_digitalInOuts); i++)
+        _digitalInOuts[i] = NULL;
+
+    // Servo
+    for (size_t i = 0; i < sizeof(_servos) / sizeof(*_servos); i++)
+        _servos[i] = NULL;
+
+    // MPU6050
+    _mpu6050 = NULL;
+
+    // Interrup-in counters
+    for (size_t i = 0; i < sizeof(_intrInCounters) / sizeof(*_intrInCounters); i++)
+        _intrInCounters[i] = NULL;
+
+    // DC motors
+    for (size_t i = 0; i < sizeof(_dcMotors) / sizeof(*_dcMotors); i++)
+        _dcMotors[i] = NULL;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void ScilabSerial::onRx()
+{
+    // If the '\n' character was used as 'packet-end' delimiter then we could have:
+    //
+    //    while (readable()) {
+    //        volatile char   c = _base_getc();
+    //        if (c != '\n') {
+    //            if (rxBufLen >= RX_MAX)
+    //                rxBufLen = 0;                   // rxBuf overflow
+    //            rxBuf[rxBufLen++] = c;
+    //        }
+    //        else {
+    //            // packet complete
+    //            memset(rxData, 0, RX_MAX);          // clear rxData
+    //            memcpy(rxData, rxBuf, rxBufLen);    // copy rxBuf to rxData
+    //            rxDataLen = rxBufLen;               // set rxData length
+    //            memset(rxBuf, 0, RX_MAX);           // clear rxBuf
+    //            rxBufLen = 0;
+    //        }
+    //    }
+    // Unfortunately there is neither packet length information in the packet
+    // nor 'packet-end' delimiter is used.
+    // That's why we have to figure out the packet length on the fly based on it's content:
+    volatile char   c = _base_getc();       // Read a byte from UART
+
+    _rxBuf[_rxBufLen++] = c;                // Store the byte in the receive buffer
+
+    switch (_rxBuf[0]) {
+        case 'A':                           // Analog read
+        case 'G':                           // MPU6050
+        case 'R':                           // Revision request
+            if (_rxBufLen == 2)
+                _rxComplete = true;
+            break;
+
+        case 'W':                           // PWM (analog) write
+        case 'I':                           // Interrupt-in counter
+            if (_rxBufLen == 3)
+                _rxComplete = true;
+            break;
+
+        case 'D':                           // Digital read/write
+            if (_rxBufLen == 3)
+                if (_rxBuf[1] == 'r')
+                    _rxComplete = true;
+            break;
+
+        case 'S':                           // Servo motor
+            if (_rxBufLen == 3)
+                if (_rxBuf[1] != 'w')
+                    _rxComplete = true;
+            break;
+
+        case 'E':                           // Encoder
+            if (_rxBufLen == 3)
+                if (_rxBuf[1] != 'a')
+                    _rxComplete = true;
+            break;
+
+        case 'C':
+            if (_rxBufLen == 5)
+                _rxComplete = true;
+
+        case 'M':                           // DC motor
+            if (_rxBufLen == 3)
+                if (_rxBuf[1] == 'r')
+                    _rxComplete = true;
+            break;
+    }
+
+    if ((_rxBuf[0] != 'C') && (_rxBufLen == 4))
+        _rxComplete = true;
+
+    if (_rxComplete) {
+        _rxComplete = false;
+        memset(_rxData, 0, RX_MAX);         // reset rxData
+        memcpy(_rxData, _rxBuf, _rxBufLen); // copy rxBuf to rxData
+        _rxDataLen = _rxBufLen;             // set rxData length
+        memset(_rxBuf, 0, RX_MAX);          // reset rxBuf
+        _rxBufLen = 0;
+    }
+
+//    dbgPort.write((void*)(& _rxData), _rxDataLen);
+//    dbgPort.write("\r\n", 2);
+
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+ssize_t ScilabSerial::write(const void* buffer, size_t length)
+{
+    const char*     ptr = (const char*)buffer;
+    const char*     end = ptr + length;
+
+    lock();
+    while (ptr != end) {
+        _base_putc(*ptr++);
+    }
+
+    unlock();
+
+    return ptr - (const char*)buffer;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void ScilabSerial::run()
+{
+    while (1) {
+        if (_mpu6050Available)
+            mpu6050UpdateData();
+
+        if (_rxDataLen > 0) {
+            _rxDataLen = 0;
+            switch (_rxData[0]) {
+                case 'A':   // Analog read
+                    analogRead();
+                    break;
+
+                case 'W':   // PWM (analog) write
+                    pwmWrite();
+                    break;
+
+                case 'D':   // Digital
+                    digitalReadWrite();
+                    break;
+
+                case 'S':   // Servo motor
+                    servo();
+                    break;
+
+                case 'G':   // MPU6050
+                    mpu6050();
+                    break;
+
+                case 'I':   // Interrupt-in counter
+                    intrInCounter();
+                    break;
+
+                case 'E':   // Encoder
+                    encoder();
+                    break;
+
+                case 'C':   // DC motor init
+                    dcMotorInit();
+                    break;
+
+                case 'M':   // DC motor
+                    dcMotor();
+                    break;
+
+                case 'R':   // Revision
+                    revision();
+                    break;
+            }
+        }
+    }
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void ScilabSerial::analogRead()
+{
+    int     pin = _rxData[1] - '0';                     // analog pin number 0 .. 5
+    uint8_t i = pin;                                    // index of corresponding analogIn 0 .. 5
+
+    if ((pin >= 0 && pin <= 5) && (_analogIns[i] != NULL)) {
+        uint16_t    word = _analogIns[i]->read_u16();   // get decimal value 0 .. 65535
+        float       val = word / 64.0f;                 // map 16bit range to 10bit range
+
+        word = round(val);
+        write((uint8_t*) &word, sizeof(uint16_t));      // send it to Scilab
+    }
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void ScilabSerial::pwmWrite()
+{
+    int     pin = _rxData[1] - '0';                 // digital pin number 4 .. 7
+    uint8_t i = pin - 4;                            // index of corresponding analogOut (PWMOut) 0 .. 3
+
+    if ((pin >= 4 && pin <= 7) && (_pwmOuts[i] != NULL)) {
+        uint8_t val = _rxData[2];                   // the duty cycle: between 0 (always off) and 255 (always on)
+        float   dutyCycle = (float)val / 255.0f;    // 0.0f (representing on 0%) and 1.0f (representing on 100%)
+
+        _pwmOuts[i]->write(dutyCycle);
+    }
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void ScilabSerial::digitalReadWrite()
+{
+    int     pin = _rxData[2] - '0';         // digital pin number 8 .. 13
+    uint8_t i = pin - 8;                    // index of corresponding digitalInOut 0 .. 7
+
+    if ((pin >= 8 && pin <= 13) && (_digitalPinName[pin] != NC)) {
+        char            cmd = _rxData[1];   // command
+        PinDirection    pinDirection;       // pin direction (PIN_INPUT, PIN_OUTPUT)
+        char            c;
+        uint8_t         val;
+
+        switch (cmd) {
+            case 'a':                       // 'a'-> activate
+                pinDirection = (PinDirection) (_rxData[3] - '0');
+                if (_digitalInOuts[i] != NULL)
+                    delete _digitalInOuts[i];
+                _digitalInOuts[i] = new DigitalInOut(_digitalPinName[pin], pinDirection, PullUp, 0);
+                break;
+
+            case 'r':                       // 'r'-> read
+                c = _digitalInOuts[i]->read() + '0';
+                write((uint8_t*) &c, 1);
+                break;
+
+            case 'w':                       // 'w'-> write
+                val = _rxData[3] - '0';
+
+                if (val == 0 || val == 1)
+                    _digitalInOuts[i]->write(val);
+                break;
+        }
+    }
+    else
+        _digitalInOuts[i] = NULL;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void ScilabSerial::servo()
+{
+    int     pin = _rxData[2] - '0' + 1; // digital pin number 2 .. 3
+    uint8_t i = pin - 2;                // index of corresponding servo 0 .. 1
+    uint8_t val;
+
+    if ((pin == 2 || pin == 3) && (_digitalPinName[pin] != NC)) {
+        char    cmd = _rxData[1];
+        switch (cmd) {
+            case 'a':                   // 'a' -> activate servo
+                if (_servos[i] != NULL)
+                    delete _servos[i];
+                _servos[i] = new Servo(_digitalPinName[pin]);
+                break;
+
+            case 'd':                   // 'd'-> delete servo
+                delete _servos[i];
+                _servos[i] = NULL;
+                break;
+
+            case 'w':                   // 'w'-> write to servo position in degree (0 .. 180)
+                val = _rxData[3];
+                if (val >= 0 && val <= 180)
+                    _servos[i]->position(val);
+                break;
+        }
+    }
+    else
+        _servos[i] = NULL;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void ScilabSerial::mpu6050()
+{
+    uint8_t cmd = _rxData[1];
+    uint16_t data[9];
+
+    if (I2C_SCL != NC && I2C_SDA != NC) {
+        switch (cmd) {
+            case 'a':                       // 'a'-> acivate
+                if (_mpu6050 != NULL)
+                    delete _mpu6050;
+                _mpu6050 = new MPU6050();
+                _mpu6050Available = _mpu6050->init();
+                _mpu6050Timer.start();
+                break;
+
+            case 't':                       // 't' -> test
+                if (_mpu6050->selfTestOK()) {
+                    _mpu6050->reset();      // Reset registers to default in preparation for device calibration
+                    _mpu6050->calibrate();  // Calibrate gyro and accelerometers, load biases in bias registers
+                    _mpu6050Available = _mpu6050->init();
+                    _mpu6050Timer.reset();
+                    _mpu6050Timer.start();
+                    write("OK", 2);
+                }
+                else {
+                    _mpu6050Available = false;
+                    write("KO", 2);
+                }
+                break;
+
+            case 'r':                       // 'r'-> read
+                if (_mpu6050Available) {
+                    data[0] = _mpu6050->yaw();
+                    data[1] = _mpu6050->pitch();
+                    data[2] = _mpu6050->roll();
+                    data[3] = _mpu6050->accelX;
+                    data[4] = _mpu6050->accelY;
+                    data[5] = _mpu6050->accelZ;
+                    data[6] = _mpu6050->gyroX;
+                    data[7] = _mpu6050->gyroY;
+                    data[8] = _mpu6050->gyroZ;
+                    write(data, sizeof(data));  // send data to scilab
+                }
+                break;
+        }
+    }
+    else
+        _mpu6050 = NULL;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void ScilabSerial::mpu6050UpdateData()
+{
+    // check if data is available after interrupt
+
+    if (_mpu6050->dataReady()) {
+        _mpu6050->accel();
+        _mpu6050->gyro();
+
+        uint32_t    _now = _mpu6050Timer.read_us();
+        _mpu6050Timer.reset();
+
+        float   _deltaT = float(_now / 1e-6);   // set integration time in seconds
+
+        _mpu6050->madgwickFilter(_deltaT);      // apply filter to estimate yaw, pitch and roll
+    }
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void ScilabSerial::intrInCounter()
+{
+    int     pin = _rxData[2] - '0'; // digital pin number 8 .. 13
+    uint8_t i = pin - 8;            // index of corresonding IntCounter 0 .. 7
+
+    if ((pin >= 8 && pin <= 13) && (_digitalPinName[pin] != NC)) {
+        uint8_t val;
+        char    cmd = _rxData[1];
+        switch (cmd) {
+            case 'a':               // 'a' -> activate a counter
+                if (_intrInCounters[i] != NULL)
+                    delete _intrInCounters[i];
+                _intrInCounters[i] = new IntrInCounter(_digitalPinName[pin]);
+                break;
+
+            case 'p':               // 'p' -> get position of a counter
+                val = _intrInCounters[i]->read();
+                write(&val, 1);     // send value to scilab (0 .. 256)
+                break;
+
+            case 'r':               // 'r' -> release an interrupt counter
+                delete _intrInCounters[i];
+                break;
+
+            case 'z':               // 'z' -> set to zero (reset) a counter
+                _intrInCounters[i]->reset();
+                break;
+        }
+    }
+    else
+        _intrInCounters[i] = NULL;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void ScilabSerial::encoder()
+{
+    uint8_t i = _rxData[2] - '0';           // encoder index 0 .. 1
+
+    if ((i >= 0 && i <= 1) && (_digitalPinName[i])) {
+        int     pinA, pinB;
+        int8_t  pos;
+        int     mode;
+        char    cmd = _rxData[1];           // command
+
+        switch (cmd) {
+            case 'a':                       // 'a' -> activate an encoder
+                i == 0 ? pinA = 8 : pinA = 10;
+                pinB = pinA + 1;            // pinB -> 9 or 11
+                mode = _rxData[3] - '0';    // '1' -> up chanA, '2' -> up/down chanA, '4' -> up/down chanA and chanB
+                if (_encoders[i] != NULL)
+                    delete _encoders[i];
+                _encoders[i] = new Encoder(_digitalPinName[pinA], _digitalPinName[pinB], mode);
+                break;
+
+            case 'p':                       // 'p' -> get position of an encoder
+                pos = _encoders[i]->position();
+
+                write(&pos, 1);             // send value to scilab (0 .. 256)
+                break;
+
+            case 'r':                       // 'r' -> release an encoder
+                delete _encoders[i];
+                break;
+
+            case 'z':                       // 'z' -> set to zero (reset) the position of an encoder
+                _encoders[i]->reset();
+                break;
+        }
+    }
+    else
+        _encoders[i] = NULL;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void ScilabSerial::dcMotorInit()
+{
+    uint8_t pin1, pin2;
+    uint8_t driverType;
+    uint8_t i = _rxData[1] - '0';               // motor index
+
+    if ((i < 2) && (_digitalPinName[i] != NC)) {
+        pin1 = _rxData[2];                      // pin1 is 2 or 4
+        if (pin1 == 2 || pin1 == 4) {
+            pin2 = _rxData[3] - '0';            // pin2 is 3 or 5
+            if (pin2 == 3 || pin2 == 5) {
+                driverType = _rxData[4] - '0';  //  driver: 0 -> L293, 1 -> L298
+                if (driverType >= 0 && driverType <= 1) {
+                    if (_dcMotors[i] != NULL)
+                        delete _dcMotors[i];
+                    _dcMotors[i] = new DcMotor(_digitalPinName[pin1], _digitalPinName[pin2], driverType);
+                    write("OK", 2);             // tell scilab that motor initialization is complete
+                }
+            }
+        }
+    }
+    else
+        _dcMotors[i] = NULL;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void ScilabSerial::dcMotor()
+{
+    int     pin1, pin2;
+    int     mode;
+    char    cmd;
+    float   speed;
+    uint8_t i = _rxData[1] - '0';   // motor index
+
+    if ((i < 2) && (_dcMotors[i] != NULL)) {
+        cmd = _rxData[2] - '0';     // direction of rotation or release
+        switch (cmd) {
+            case 0:                 // clockwise
+            case 1:                 // counter clockwise
+                speed = _rxData[3]; // speed
+                _dcMotors[i]->run(cmd, speed);
+                break;
+
+            case 'r':               // release
+                delete _dcMotors[i];
+                break;
+        }
+    }
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void ScilabSerial::revision()
+{
+    write("v5", 2);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ScilabSerial/ScilabSerial.h	Mon Jan 18 19:51:22 2021 +0000
@@ -0,0 +1,103 @@
+#ifndef SCILABSERIAL_H
+#define SCILABSERIAL_H
+
+#include "mbed.h"
+#include "Servo.h"
+#include "IntrInCounter.h"
+#include "Encoder.h"
+#include "DcMotor.h"
+#include "MPU6050.h"
+
+//#define USBSERIAL
+#define RX_MAX  64
+
+#ifdef USBSERIAL
+#include "USBSerial.h"
+//#include "stm32f103c8t6.h"
+#endif
+/**
+ * @brief   The PcSerial class
+ * @note    For serial communication with PC
+ */
+#ifdef USBSERIAL
+class PcSerial :
+    public USBSerial
+{
+public:
+    PcSerial() :
+    USBSerial(0x1f00, 0x2012, 0x0001, false)
+    {
+        rxDataLen = 0;
+        attach(this, &PcSerial::onRx);
+    }
+
+    void onRx()
+    {
+        memset(rxData, 0, RX_MAX);
+        while (available()) {
+            char    c = _getc();
+            if (c != '\n') {
+                if (rxDataLen >= RX_MAX)
+                    rxDataLen = 0;  // rxBuf overflow
+                rxData[rxDataLen++] = c;
+            }
+        }
+    }
+
+    ssize_t         write(const void* buffer, size_t length)    { return writeBlock((uint8_t*)buffer, length); }
+    char            rxData[RX_MAX];
+    volatile size_t rxDataLen;
+};
+#else
+class ScilabSerial :
+    public SerialBase
+{
+    bool            _rxComplete;
+    char            _rxBuf[RX_MAX];
+    volatile size_t _rxBufLen;
+    char            _rxData[RX_MAX];
+    volatile size_t _rxDataLen;
+
+#if defined(TARGET_BLUEPILL)
+    PinName         _digitalPinName[16] = { NC, NC, D2, D3, D4, D5, D6, D7, D8, NC, D10, D11, D12, D13, D14, D15 };
+    PinName         _analogPinName[6] = { A0, A1, A2, A3, NC, NC };
+#elif defined(TARGET_BLACKPILL)
+    PinName         _digitalPinName[16] = { NC, NC, D2, D3, D4, D5, D6, D7, D8, NC, D10, D11, D12, D13, D14, D15 };
+    PinName         _analogPinName[6] = { A0, A1, A2, A3, NC, NC };
+#else
+    PinName         _digitalPinName[16] = { NC, NC, D2, D3, D4, D5, D6, D7, D8, D9, D10, D11, D12, D13, D14, D15 };
+    PinName         _analogPinName[6] = { A0, A1, A2, A3, A4, A5 };
+#endif
+    DigitalInOut*   _digitalInOuts[8];
+    AnalogIn*       _analogIns[6];
+    PwmOut*         _pwmOuts[4];
+    IntrInCounter*  _intrInCounters[8];
+    Servo*          _servos[2];
+    Encoder*        _encoders[2];
+    DcMotor*        _dcMotors[2];
+    MPU6050*        _mpu6050;
+    Timer           _mpu6050Timer;
+    uint32_t        _now;
+    float           _deltaT;
+    bool            _mpu6050Available;
+public:
+
+    ScilabSerial(PinName tx, PinName rx, int baud = 115200);
+
+    void    run();
+    void    onRx();
+    ssize_t write(const void* buffer, size_t length);
+    void    analogRead();
+    void    pwmWrite();
+    void    digitalReadWrite();
+    void    servo();
+    void    mpu6050();
+    void    intrInCounter();
+    void    encoder();
+    void    dcMotorInit();
+    void    dcMotor();
+    void    revision();
+    void    mpu6050UpdateData();
+};
+#endif
+#endif // SCILABSERIAL_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Servo.lib	Mon Jan 18 19:51:22 2021 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/simon/code/Servo/#36b69a7ced07
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Mon Jan 18 19:51:22 2021 +0000
@@ -0,0 +1,15 @@
+#include "mbed.h"
+#include "ScilabSerial.h"
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+int main()
+{
+    ScilabSerial    scilab(USBTX, USBRX);
+
+    scilab.run();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-os.lib	Mon Jan 18 19:51:22 2021 +0000
@@ -0,0 +1,1 @@
+https://github.com/ARMmbed/mbed-os/#f2278567d09b9ae9f4843e1d9d393526b9462783
Binary file resources/official_armmbed_example_badge.png has changed