![](/media/cache/profiles/7c39a5f991bef4a1e34187677a65871d.jpg.50x50_q85.jpg)
Data acquisition and device control with Scilab.
Desciription
Scilab is a freeware alternative to MATLAB. For low-cost data acquisition and device control a nice Arduino toolbox is available.
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.
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
- 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
- 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.
PID controller
- In Xcos open
examples/Arduino9.zcos
ScilabSerial/ScilabSerial.cpp
- Committer:
- hudakz
- Date:
- 2021-01-18
- Revision:
- 0:295b7e1c12f3
File content as of revision 0:295b7e1c12f3:
#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); }