Перейти к основному содержимому

PHP

Идем на сайт php.net, выбираем Windows downloads и скачиваем Zip релиз. На момент написания статьи 8.2.1.

Распаковываем скачанный архив командой
mkdir php-8.2.1 && tar -x -f php-8.2.1-nts-Win32-vs16-x64.zip -C php-8.2.1

Переходим в распакованную директорию

cd 8.2.1

Создайте php.ini для конфигурации из готового шаблона

copy php.ini-development php.ini

Откройте php.ini через текстовый редактор и раскомментируйте следующие строки:

extension_dir = "ext"
extension=ffi
Измените значение переменной short_open_tag=Off на On
Добавьте в переменную окружения PATH путь к папке php-8.2.1.

Подключение RISDK к приложению на PHP

С помощью расширения FFI подключаем распределенную библиотеку risdk, для этого нам в конструкторе cdef нужно указать пути к файлам .so или .dll и .h

Создадим php файл

type nul > shared_client.php

Пример подключения библиотеки, инициализации SDK и создания компонента

Структура проекта

. workspace/
--- > shared_client.php
--- > librisdk.h
--- > librisdk.so/.dll
--- > libusb-1.0.so/.dll

Подключим библиотеку

Обратите внимание на значение переменной $RELATIVE_PATH. Это относительный путь для поиска библиотеки, если она лежит ниже чем php скрипт.
Например при значении '/..' поиск библиотеки будет выполняться на одну директорию ниже.
При значении '' файлы librisdk.h и librisdk.so должны располагаться в той же директории что и php скрипт.

<?
$RELATIVE_PATH = '';
$headers = file_get_contents(__DIR__ . $RELATIVE_PATH . '/librisdk.h');
// Вырежем лишнии заголовки
$headers = preg_replace(['/#ifdef __cplusplus\s*extern "C" {\s*#endif/i', '/#ifdef __cplusplus\s*}\s*#endif/i'], '', $headers);
$ffi = FFI::cdef($headers, __DIR__ . $RELATIVE_PATH . '/librisdk.so');

print("Success \n");

?>

Для запуска скрипта выполним в терминале следующую команду:

php shared_client.php

Создадим компонент

$logLevel = 2;
$errorText = $ffi->new('char[1000]', 0); // Выделяем память на строку с ошибкой

// Инициализируем SDK
$errCode = $ffi->RI_SDK_InitSDK($logLevel, $errorText);
if ($errCode) {
// Выведем текст и код ошибки в терминал, при возникновении
print("RI_SDK_InitSDK errorText:" . FFI::string($errorText) . " errCode: " . $errCode . " \n");
return $errCode;
}

// Создаем базовый компонент
$descriptor = $ffi->new('int', 0); // Выделяем память на переменную с номером дескриптора
$errCode = $ffi->RI_SDK_CreateBasic(FFI::addr($descriptor), $errorText);
if ($errCode) {
// Выведем текст и код ошибки в терминал, при возникновении
print("RI_SDK_CreateBasic errorText:" . FFI::string($errorText). " errCode: " . $errCode . " \n");
return $errCode;
}

// Выведем полученный дескриптор компонента
print('descriptor: ' . + $descriptor->cdata . " \n");

Запустим скрипт

 php shared_client.php

Результат успешной работы должен выглядеть следующим образом:

descriptor: 1
Success

Пример программы на PHP использующий RI_SDK

Описание

Данная программа является примером, использования библиотеки. Она реализует простой сценарий поведения робота. Сперва все сервоприводы встают в условное стартовое положение. После робот берет кубик с позиции слева и перемещает его на позицию справа. После сервоприводы возвращаются в первоначальное положение.

Одновременно с движением робота, происходит свечение светодиода. Светодиод горит красным цветом пока сервоприводы приводятся к стартовому положению, после загорается зеленым. Далее светодиоды моргает зеленым при передвижении тела робота и мерцает синим, пока робот кладет/берет кубик.

Структура проекта

. workspace/
--- > demo.php
--- > librisdk.h
--- > librisdk.dll/.so
--- > CH341DLL.DLL/ CH341DLLA64.DLL
--- > SLABHIDDevice.dll
--- > SLABHIDtoSMBus.dll
--- > libusb-1.0.dll/.so

Реализация

<?
$GLOBALS = ['body_start_pulse' => 1500, // стартовая позиция тела
'arrowR_start_pulse' => 2000, // стартовая возиция правой стрелы
'arrowL_start_pulse' => 1000, // стартовая позиция левой стрелы
'claw_start_pulse' => 1000, // стартовая позиция клешни
'claw_rotate_start_pulse' => 1500, // стартовая позиция поворота клешни

'arrowR_over_cube_position' => -30, // позиция правой стрелы над кубиком
'arrowL_over_cube_position' => -10, // позиция левой стрелы над кубиком
'claw_unclenched_position' => 80, // позиция открытой клешни
'arrowR_cube_position' => -75, // позиция правой стрелы на месте кубика
'arrowL_cube_position' => 55, // позиция левой стрелы на месте кубика
];

// структура дескрипторов устройства
class device
{
public $i2c; // дескриптор i2c
public $pwm; // дескриптор pwm
public $body; // дескриптор сервопривода тела
public $claw; // дескриптор сервопривода клешни
public $arrowR; // дескриптор сервопривода правой стрелы
public $arrowL; // дескриптор сервопривода левой стрелы
public $clawRotate; // дескриптор сервопривода поворота клешни
public $led; // дескриптор светодиода

public function __construct($i2c, $pwm, $body, $claw, $arrowR, $arrowL, $clawRotate, $led)
{
$this->i2c = $i2c;
$this->pwm = $pwm;
$this->body = $body;
$this->claw = $claw;
$this->arrowR = $arrowR;
$this->arrowL = $arrowL;
$this->clawRotate = $clawRotate;
$this->led = $led;
}
}

// для хранения дескриптоов сервоприводов
class servo
{
public $descriptor; // дескриптор сервопривода
public $startPosition; // стартовая позиция сервопривода

public function __construct($descriptor, $startPosition)
{
$this->descriptor = $descriptor;
$this->startPosition = $startPosition;
}
}

// Обратите внимание на значение переменной $RELATIVE_PATH
// Это относительный путь для поиска библиотеки, если она лежит ниже чем php скрипт
// Например при значении '/..' поиск библиотеки будет выполняться на одну директорию ниже
// При значении '' файлы librisdk.h и librisdk.so должны располагаться в той же директории что и php скрипт
$RELATIVE_PATH = '';
$headers = file_get_contents(__DIR__ . $RELATIVE_PATH . '/librisdk.h');
// Вырежем лишнии заголовки
$headers = preg_replace(['/#ifdef __cplusplus\s*extern "C" {\s*#endif/i', '/#ifdef __cplusplus\s*}\s*#endif/i'], '', $headers);
$ffi = FFI::cdef($headers, __DIR__ . $RELATIVE_PATH . '/librisdk.dll');
$errorText = $ffi->new('char[1000]', 0); // Выделяем память на строку с ошибкой. Передается как входной параметр,при возникновении ошибки в эту переменную будет записан текст ошибки

// инициализация структуры устройства
$robohand = new Device($ffi->new('int', 0), $ffi->new('int', 0), $ffi->new('int', 0), $ffi->new('int', 0), $ffi->new('int', 0),
$ffi->new('int', 0), $ffi->new('int', 0), $ffi->new('int', 0));

// массив дескрипторов сервоприводов
$servos = [
new servo($robohand->body, $GLOBALS['body_start_pulse']),
new servo($robohand->claw, $GLOBALS['claw_start_pulse']),
new servo($robohand->arrowR, $GLOBALS['arrowR_start_pulse']),
new servo($robohand->arrowL, $GLOBALS['arrowL_start_pulse']),
new servo($robohand->clawRotate, $GLOBALS['claw_rotate_start_pulse'])
];

$errCode = start($ffi, $servos, $robohand, $errorText);
if ($errCode) {
print("ErrorText: " . FFI::string($errorText) . " ErrCode: " . $errCode . " \n");

}

// start - запускает демо программу
function start($ffi, $servos, $robohand, $errorText)
{
$speed = 100; // скорость в градусах в секунду

// initDevice - инициализация библиотеки и устройств
$errCode = initDevice($ffi, $servos, $robohand, $errorText);
if ($errCode) {
return $errCode;
}

// startPositionAllServo - переводит все сервоприводы в стартовую позицию
$errCode = startPositionAllServo($ffi, $robohand, $servos, $errorText);
if ($errCode) {
return $errCode;
}

// Двигаем тело к местонахождению кубика
$errCode = rotateBody($ffi, $robohand, 45, $speed, $errorText);
if ($errCode) {
return $errCode;
}

// Берем кубик
$errCode = get($ffi, $robohand, $speed, $errorText);
if ($errCode) {
return $errCode;
}

//Двигаем тело к новому местонахождению кубика
$errCode = rotateBody($ffi, $robohand, -90, $speed, $errorText);
if ($errCode) {
return $errCode;
}

//Кладем кубик
$errCode = put($ffi, $robohand, $speed, $errorText);
if ($errCode) {
return $errCode;
}

// Возвращаем тело в стартовое положение
$errCode = rotateBody($ffi, $robohand, 45, $speed, $errorText);
if ($errCode) {
return $errCode;
}

// Уничтожаем компоненты и библиотеку
$errCode = destruct($ffi, $robohand, $servos, $errorText);
if ($errCode) {
return $errCode;
}

print("Success \n");

return 0;
}

// get - берет кубик
function get($ffi, $robohand, $speed, $errorText)
{
//выполняем мерцание светодиодом,передаем дескриптор светодиода,3 параметра цвета(RGB),продолжительность,кол-во повторений и включаем асинхронный режим работы
$errCode = $ffi->RI_SDK_exec_RGB_LED_Flicker($robohand->led->cdata, 0, 0, 255, 500, 0, true, $errorText);
if ($errCode) {
return $errCode;
}

//выполняем поворот на заданный угол,передаем дескриптор стрелы,угол,скорость и асинхронный режим работы
$errCode = $ffi->RI_SDK_exec_ServoDrive_Turn($robohand->arrowR->cdata, $GLOBALS['arrowR_over_cube_position'], $speed, false, $errorText);
if ($errCode) {
return $errCode;
}

//выполняем поворот на заданный угол,передаем дескриптор стрелы,угол,скорость и асинхронный режим работы
$errCode = $ffi->RI_SDK_exec_ServoDrive_Turn($robohand->arrowL->cdata, $GLOBALS['arrowL_over_cube_position'], $speed, false, $errorText);
if ($errCode) {
return $errCode;
}

//выполняем поворот на заданный угол,передаем дескриптор клешни,угол,скорость и асинхронный режим работы
$errCode = $ffi->RI_SDK_exec_ServoDrive_Turn($robohand->claw->cdata, $GLOBALS['claw_unclenched_position'], $speed, false, $errorText);
if ($errCode) {
return $errCode;
}

//выполняем поворот на заданный угол,передаем дескриптор стрелы,угол,скорость и асинхронный режим работы
$errCode = $ffi->RI_SDK_exec_ServoDrive_Turn($robohand->arrowR->cdata, ($GLOBALS['arrowR_cube_position'] - $GLOBALS['arrowR_over_cube_position']), $speed, false, $errorText);
if ($errCode) {
return $errCode;
}

//выполняем поворот на заданный угол,передаем дескриптор стрелы,угол,скорость и асинхронный режим работы
$errCode = $ffi->RI_SDK_exec_ServoDrive_Turn($robohand->arrowL->cdata, ($GLOBALS['arrowL_cube_position'] - $GLOBALS['arrowL_over_cube_position']), $speed, false, $errorText);
if ($errCode) {
return $errCode;
}

//выполняем поворот на заданный угол,передаем дескриптор клешни,угол,скорость и асинхронный режим работы
$errCode = $ffi->RI_SDK_exec_ServoDrive_Turn($robohand->claw->cdata, (-1) * $GLOBALS['claw_unclenched_position'], $speed, false, $errorText);
if ($errCode) {
return $errCode;
}

//выполняем поворот на заданный угол,передаем дескриптор стрелы,угол,скорость и асинхронный режим работы
$errCode = $ffi->RI_SDK_exec_ServoDrive_Turn($robohand->arrowR->cdata, (-1) * $GLOBALS['arrowR_cube_position'], $speed, false, $errorText);
if ($errCode) {
return $errCode;
}

//выполняем поворот на заданный угол,передаем дескриптор стрелы,угол,скорость и асинхронный режим работы
$errCode = $ffi->RI_SDK_exec_ServoDrive_Turn($robohand->arrowL->cdata, (-1) * $GLOBALS['arrowL_cube_position'], $speed, false, $errorText);
if ($errCode) {
return $errCode;
}

return 0;
}

// rotateBody - вращение тела в указанный угол
function rotateBody($ffi, $robohand, $angle, $speed, $errorText)
{
//выполняем мигание с заданной частотой,передаем дескриптор светодиода,3 параметра цвета(RGB),частоту,продолжительность и включаем асинхронный режим работы
$errCode = $ffi->RI_SDK_exec_RGB_LED_FlashingWithFrequency($robohand->led->cdata, 0, 255, 0, 5, 0, true, $errorText);
if ($errCode) {
return $errCode;
}

//выполняем поворот на заданный угол,передаем дескриптор тела,угол,скорость и асинхронный режим работы
$errCode = $ffi->RI_SDK_exec_ServoDrive_Turn($robohand->body->cdata, $angle, $speed, false, $errorText);
if ($errCode) {
return $errCode;
}

//выполняем поворот на заданный угол,передаем дескриптор клешни,угол,скорость и асинхронный режим работы
$errCode = $ffi->RI_SDK_exec_ServoDrive_Turn($robohand->clawRotate->cdata, 45, $speed, false, $errorText);
if ($errCode) {
return $errCode;
}

//выполняем поворот на заданный угол,передаем дескриптор клешни,угол,скорость и асинхронный режим работы
$errCode = $ffi->RI_SDK_exec_ServoDrive_Turn($robohand->clawRotate->cdata, -45, $speed, false, $errorText);
if ($errCode) {
return $errCode;
}

return 0;
}


// initServos - создает сервоприводы и линкует их
function initServos($ffi, $robohand, $servos, $errorText)
{
//создаем 5 сервоприводов и линкуем их к пинам 0-4
for ($i = 0; $i < 5; $i++) {
// создаем компонент сервопривода с конкретной моделью как исполняемое устройство и получаем дескриптор сервопривода
$errCode = $ffi->RI_SDK_CreateModelComponent("executor", "servodrive", "mg90s", FFI::addr($servos[$i]->descriptor), $errorText);
if ($errCode) {
return $errCode;
}
//связываем сервопривод с ШИМ,передаем дескриптор сервопривода и шим
$errCode = $ffi->RI_SDK_LinkServodriveToController($servos[$i]->descriptor->cdata, $robohand->pwm->cdata, $i, $errorText);
if ($errCode) {
return $errCode;
}
}

return 0;
}

// initDevice - инициализация библиотеки и устройств
function initDevice($ffi, $servos, $robohand, $errorText)
{
$logLevel = 2; // уровень логирования

// Инициализируем SDK
$errCode = $ffi->RI_SDK_InitSDK($logLevel, $errorText);
if ($errCode) {
return $errCode;
}

// создаем компонент ШИМ с конкретной моделью как исполняемое устройство,получаем дескриптор сервопривода
$errCode = $ffi->RI_SDK_CreateModelComponent("connector", "pwm", "pca9685", FFI::addr($robohand->pwm), $errorText);
if ($errCode) {
return $errCode;
}

// создаем компонент i2c адатера
// Здесь осуществлен примитивное определение подключенной модели адаптера
// Сначала пробуем создать i2c адаптер модели ch341 и связать с ним ШИМ
$errCode = $ffi->RI_SDK_CreateModelComponent("connector", "i2c_adapter", "ch341", FFI::addr($robohand->i2c), $errorText);
if ($errCode) {
return $errCode;
}

//связываем i2c адаптер с ШИМ по адресу 0x40
$errCode = $ffi->RI_SDK_LinkPWMToController($robohand->pwm->cdata, $robohand->i2c->cdata, 0x40, $errorText);
if ($errCode) {
// Если не получается то пробуем создать i2c адаптер модели cp2112
$errCode = $ffi->RI_SDK_CreateModelComponent("connector", "i2c_adapter", "cp2112", FFI::addr($robohand->i2c), $errorText);
if ($errCode) {
return $errCode;
}

$errCode = $ffi->RI_SDK_LinkPWMToController($robohand->pwm->cdata, $robohand->i2c->cdata, 0x40, $errorText);
if ($errCode) {
return $errCode;
}
}

// создаем компонент светодиода с конкретной моделью (ky016) как исполняемое устройство и получаем дескриптор светодиода
$errCode = $ffi->RI_SDK_CreateModelComponent("executor", "led", "ky016", FFI::addr($robohand->led), $errorText);
if ($errCode) {
return $errCode;
}

//связываем светодиод адаптер с ШИМ,передаем значения трех пинов к которым подключен светодиод
$errCode = $ffi->RI_SDK_LinkLedToController($robohand->led->cdata, $robohand->pwm->cdata, 15, 14, 13, $errorText);
if ($errCode) {
return $errCode;
}

//инициализируем сервоприводы
$errCode = initServos($ffi, $robohand, $servos, $errorText);
if ($errCode) {
return $errCode;
}

return 0;
}

// startPositionAllServo - переводит все сервоприводы в стартовую позицию
function startPositionAllServo($ffi, $robohand, $servos, $errorText)
{
//выполняем одиночное свечение светодиодом,передаем дескриптор светодиода,3 параметра цвета(RGB), и включаем асинхронный режим работы
$errCode = $ffi->RI_SDK_exec_RGB_LED_SinglePulse($robohand->led->cdata, 255, 0, 0, 0, true, $errorText);
if ($errCode) {
return $errCode;
}

//приводим сервоприводы в стартовое положение
for ($i = 0; $i < 5; $i++) {
//выполняем поворот сервопривода в заданный угол,передаем дескриптор сервоприовда,значение угла
$errCode = $ffi->RI_SDK_exec_ServoDrive_TurnByPulse($servos[$i]->descriptor->cdata, $servos[$i]->startPosition, $errorText);
if ($errCode) {
return $errCode;
}
sleep(0.5);
}

//выполняем одиночное свечение светодиодом,передаем дескриптор светодиода,3 параметра цвета(RGB), и включаем асинхронный режим работы
$errCode = $ffi->RI_SDK_exec_RGB_LED_SinglePulse($robohand->led->cdata, 0, 255, 0, 0, true, $errorText);
if ($errCode) {
return $errCode;
}
sleep(0.5);
return 0;
}

// put - кладет кубик
function put($ffi, $robohand, $speed, $errorText)
{
//выполняем мерцание светодиодом,передаем дескриптор светодиода,3 параметра цвета(RGB),продолжительность,кол-во повторений и включаем асинхронный режим работы
$errCode = $ffi->RI_SDK_exec_RGB_LED_Flicker($robohand->led->cdata, 0, 0, 255, 500, 0, true, $errorText);
if ($errCode) {
return $errCode;
}

//выполняем поворот на заданный угол,передаем дескриптор стрелы,угол,скорость и асинхронный режим работы
$errCode = $ffi->RI_SDK_exec_ServoDrive_Turn($robohand->arrowR->cdata, $GLOBALS['arrowR_cube_position'], $speed, false, $errorText);
if ($errCode) {
return $errCode;
}

//выполняем поворот на заданный угол,передаем дескриптор стрелы,угол,скорость и асинхронный режим работы
$errCode = $ffi->RI_SDK_exec_ServoDrive_Turn($robohand->arrowL->cdata, $GLOBALS['arrowL_cube_position'], $speed, false, $errorText);
if ($errCode) {
return $errCode;
}

//выполняем поворот на заданный угол,передаем дескриптор клешни,угол,скорость и асинхронный режим работы
$errCode = $ffi->RI_SDK_exec_ServoDrive_Turn($robohand->claw->cdata, $GLOBALS['claw_unclenched_position'], $speed, false, $errorText);
if ($errCode) {
return $errCode;
}

//выполняем поворот на заданный угол,передаем дескриптор стрелы,угол,скорость и асинхронный режим работы
$errCode = $ffi->RI_SDK_exec_ServoDrive_Turn($robohand->arrowR->cdata, (-1) * $GLOBALS['arrowR_cube_position'], $speed, false, $errorText);
if ($errCode) {
return $errCode;
}

//выполняем поворот на заданный угол,передаем дескриптор стрелы,угол,скорость и асинхронный режим работы
$errCode = $ffi->RI_SDK_exec_ServoDrive_Turn($robohand->arrowL->cdata, (-1) * $GLOBALS['arrowL_cube_position'], $speed, false, $errorText);
if ($errCode) {
return $errCode;
}

//выполняем поворот на заданный угол,передаем дескриптор клешни,угол,скорость и асинхронный режим работы
$errCode = $ffi->RI_SDK_exec_ServoDrive_Turn($robohand->claw->cdata, (-1) * $GLOBALS['claw_unclenched_position'], $speed, false, $errorText);
if ($errCode) {
return $errCode;
}
return 0;
}

// destructServos - уничтожает сервоприводы
function destructServos($ffi, $servos, $errorText)
{
//уничтожаем сервоприводы
for ($i = 0; $i < 5; $i++) {
$errCode = $ffi->RI_SDK_DestroyComponent($servos[$i]->descriptor->cdata, $errorText);
if ($errCode) {
return $errCode;
}
}

return 0;
}

// destruct - уничтожает все компоненты и библиотеку
function destruct($ffi, $robohand, $servos, $errorText)
{
//выполняем одиночное свечение светодиодом,передаем дескриптор светодиода,3 параметра цвета(RGB), и включаем асинхронный режим работы
$errCode = $ffi->RI_SDK_exec_RGB_LED_SinglePulse($robohand->led->cdata, 255, 0, 0, 0, true, $errorText);
if ($errCode) {
return $errCode;
}

//уничтожаем сервоприводы
$errCode = destructServos($ffi, $servos, $errorText);
if ($errCode) {
return $errCode;
}

//останавливаем свечение светодиода с определенным дескриптором
$errCode = $ffi->RI_SDK_exec_RGB_LED_Stop($robohand->led->cdata, $errorText);
if ($errCode) {
return $errCode;
}

//удаляем компонент светодиода по дескриптору
$errCode = $ffi->RI_SDK_DestroyComponent($robohand->led->cdata, $errorText);
if ($errCode) {
return $errCode;
}

//сбрасываем все порты на ШИМ
$errCode = $ffi->RI_SDK_sigmod_PWM_ResetAll($robohand->pwm->cdata, $errorText);
if ($errCode) {
return $errCode;
}

// удаляем компонент ШИМ
$errCode = $ffi->RI_SDK_DestroyComponent($robohand->pwm->cdata, $errorText);
if ($errCode) {
return $errCode;
}

//удаляем компонент i2c
$errCode = $ffi->RI_SDK_DestroyComponent($robohand->i2c->cdata, $errorText);
if ($errCode) {
return $errCode;
}

//удаляем sdk со всеми компонентами в реестре компонентов
$errCode = $ffi->RI_SDK_DestroySDK(true, $errorText);
if ($errCode) {
return $errCode;
}
return 0;
}