Merge branch 'amor-selene'
Conflicts: .gitignore sinqEPICSApp/src/pmacAxis.cpp sinqEPICSApp/src/pmacAxis.h sinqEPICSApp/src/pmacController.cpp sinqEPICSApp/src/pmacController.h
This commit is contained in:
124
.gitignore
vendored
124
.gitignore
vendored
@ -1,3 +1,121 @@
|
||||
O.*
|
||||
.cvsignore
|
||||
.gitignore
|
||||
# Took these from the https://github.com/github/gitignore project on October 21, 2011
|
||||
|
||||
# **** 'Personal' entries don't belong in here - put them in your .git/info/exclude file ****
|
||||
|
||||
# Ignore text editor (e.g. emacs) autosave files
|
||||
*~
|
||||
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
*.d
|
||||
SICServer*
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
|
||||
# Compiled python files
|
||||
*.py[co]
|
||||
|
||||
# Eclipse-generated files
|
||||
*.pydevproject
|
||||
.project
|
||||
.metadata
|
||||
bin/**
|
||||
tmp/**
|
||||
tmp/**/*
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.classpath
|
||||
.settings/
|
||||
.loadpath
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# CDT-specific
|
||||
.cproject
|
||||
|
||||
# PDT-specific
|
||||
.buildpath
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
*.sln
|
||||
*.vcproj
|
||||
*.exe
|
||||
*.vcxproj
|
||||
*.filters
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.sln.docstates
|
||||
*.sdf
|
||||
|
||||
#Test results
|
||||
*.log
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Rr]elease/
|
||||
*_i.c
|
||||
*_p.c
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.vspscc
|
||||
.builds
|
||||
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*
|
||||
|
||||
|
||||
|
||||
# Others
|
||||
*.autosave
|
||||
|
||||
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
|
||||
|
||||
# Mac OS X Finder
|
||||
.DS_Store
|
||||
._*
|
||||
|
1
10-llbDevices.rules
Normal file
1
10-llbDevices.rules
Normal file
@ -0,0 +1 @@
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="04b4", ATTR{idProduct}=="1002", MODE="0666" GROUP="plugdev", TAG+="uaccess"
|
@ -2,7 +2,7 @@
|
||||
include /ioc/tools/driver.makefile
|
||||
|
||||
MODULE=sinq
|
||||
|
||||
LIBVERSION=koennecke
|
||||
BUILDCLASSES=Linux
|
||||
|
||||
# additional module dependencies
|
||||
|
6
sinqEPICSApp/Db/motorSet.db
Normal file
6
sinqEPICSApp/Db/motorSet.db
Normal file
@ -0,0 +1,6 @@
|
||||
# workaround over set position
|
||||
record(ao, "$(P)$(M)-SetPosition") {
|
||||
field(DTYP, "asynFloat64")
|
||||
field(OUT, "@asyn($(PORT),$(N),1) SET_MOTOR_POSITION")
|
||||
field(PINI, "YES")
|
||||
}
|
@ -10,8 +10,8 @@
|
||||
#include "SINQController.h"
|
||||
#include "asynMotorController.h"
|
||||
|
||||
SINQController::SINQController(const char *portName, const char *SINQPortName, int numAxes)
|
||||
: asynMotorController(portName, numAxes+1, NUM_MOTOR_DRIVER_PARAMS+2,
|
||||
SINQController::SINQController(const char *portName, const char *SINQPortName, int numAxes, const int& extraParams)
|
||||
: asynMotorController(portName, numAxes+1, NUM_MOTOR_DRIVER_PARAMS+extraParams,
|
||||
0, // No additional interfaces beyond those in base class
|
||||
0, // No additional callback interfaces beyond those in base class
|
||||
ASYN_CANBLOCK | ASYN_MULTIDEVICE,
|
||||
|
@ -17,7 +17,7 @@
|
||||
class epicsShareClass SINQController : public asynMotorController
|
||||
{
|
||||
public:
|
||||
SINQController(const char *portName, const char *SINQPortName, int numAxes);
|
||||
SINQController(const char *portName, const char *SINQPortName, int numAxes, const int& extraParams=2);
|
||||
|
||||
friend class SINQAxis;
|
||||
protected:
|
||||
|
@ -44,6 +44,9 @@
|
||||
|
||||
#define MULT 1000.
|
||||
|
||||
#define IDLEPOLL 2.
|
||||
#define BUSYPOLL .05
|
||||
|
||||
#define ABS(x) (x < 0 ? -(x) : (x))
|
||||
|
||||
extern "C" void shutdownCallback(void *pPvt)
|
||||
@ -56,9 +59,10 @@ extern "C" void shutdownCallback(void *pPvt)
|
||||
}
|
||||
|
||||
// These are the pmacAxis class methods
|
||||
pmacAxis::pmacAxis(pmacController *pC, int axisNo)
|
||||
pmacAxis::pmacAxis(pmacController *pC, int axisNo, bool enable)
|
||||
: SINQAxis(pC, axisNo),
|
||||
pC_(pC)
|
||||
pC_(pC),
|
||||
autoEnable(enable)
|
||||
{
|
||||
static const char *functionName = "pmacAxis::pmacAxis";
|
||||
|
||||
@ -80,6 +84,7 @@ pmacAxis::pmacAxis(pmacController *pC, int axisNo)
|
||||
status6Time = 0;
|
||||
starting = 0;
|
||||
homing = 0;
|
||||
next_poll = -1;
|
||||
|
||||
/* Set an EPICS exit handler that will shut down polling before asyn kills the IP sockets */
|
||||
epicsAtExit(shutdownCallback, pC_);
|
||||
@ -156,13 +161,18 @@ asynStatus pmacAxis::getAxisInitialStatus(void)
|
||||
}
|
||||
|
||||
// Enable the axis. After startup, the axis are disabled on the controller...
|
||||
sprintf(command, "M%2.2d14=1", axisNo_);
|
||||
cmdStatus = pC_->lowLevelWriteRead(axisNo_,command, response);
|
||||
if (cmdStatus ) {
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "%s: Error: enaabling axis %d failed.\n", functionName, axisNo_);
|
||||
return asynError;
|
||||
}
|
||||
// Warning: Selene lift axis should not be automatically enabled
|
||||
|
||||
if (autoEnable) {
|
||||
sprintf(command, "M%2.2d14=1\n", axisNo_);
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "Enable axis %d: %s",axisNo_,command);
|
||||
cmdStatus = pC_->lowLevelWriteRead(axisNo_,command, response);
|
||||
if (cmdStatus ) {
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "%s: Error: enaabling axis %d failed.\n", functionName, axisNo_);
|
||||
return asynError;
|
||||
}
|
||||
}
|
||||
|
||||
callParamCallbacks();
|
||||
|
||||
return asynSuccess;
|
||||
@ -202,7 +212,9 @@ asynStatus pmacAxis::move(double position, int relative, double min_velocity, do
|
||||
sprintf( command, "P%2.2d23=0 Q%2.2d01=%12.4f M%2.2d=1", axisNo_, axisNo_, realPosition, axisNo_);
|
||||
|
||||
status = pC_->lowLevelWriteRead(axisNo_,command, response);
|
||||
|
||||
|
||||
next_poll = -1;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -224,6 +236,8 @@ asynStatus pmacAxis::home(double min_velocity, double max_velocity, double accel
|
||||
status = pC_->lowLevelWriteRead(axisNo_,command, response);
|
||||
homing = 1;
|
||||
|
||||
next_poll = time(NULL) + BUSYPOLL;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -246,7 +260,8 @@ asynStatus pmacAxis::setPosition(double position)
|
||||
{
|
||||
//int status = 0;
|
||||
static const char *functionName = "pmacAxis::setPosition";
|
||||
|
||||
errlogPrintf("executing : %s\n", functionName);
|
||||
|
||||
pC_->debugFlow(functionName);
|
||||
|
||||
// Cannot do this.
|
||||
@ -276,6 +291,11 @@ asynStatus pmacAxis::poll(bool *moving)
|
||||
static const char *functionName = "pmacAxis::poll";
|
||||
char message[132];
|
||||
|
||||
// Protect against excessive polling
|
||||
if(time(NULL) < next_poll){
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
sprintf(message, "%s: Polling axis: %d", functionName, this->axisNo_);
|
||||
pC_->debugFlow(message);
|
||||
|
||||
@ -284,7 +304,13 @@ asynStatus pmacAxis::poll(bool *moving)
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s: getAxisStatus failed to return asynSuccess. Controller: %s, Axis: %d.\n", functionName, pC_->portName, axisNo_);
|
||||
}
|
||||
|
||||
|
||||
if(*moving){
|
||||
next_poll = time(NULL) + BUSYPOLL;
|
||||
} else {
|
||||
next_poll = time(NULL) + IDLEPOLL;
|
||||
}
|
||||
|
||||
callParamCallbacks();
|
||||
return status ? asynError : asynSuccess;
|
||||
}
|
||||
@ -345,8 +371,8 @@ asynStatus pmacAxis::getAxisStatus(bool *moving)
|
||||
int done = 0, posChanging = 0;
|
||||
double position = 0;
|
||||
int nvals = 0;
|
||||
int axisProblemFlag = 0;
|
||||
epicsUInt32 axErr = 0, axStat = 0, highLim = 0, lowLim= 0;
|
||||
int axisProblemFlag = 0, axStat = 0;
|
||||
epicsUInt32 axErr = 0, highLim = 0, lowLim= 0;
|
||||
char message[132], *axMessage;
|
||||
|
||||
|
||||
@ -744,6 +770,9 @@ asynStatus SeleneAxis::setPosition(double position)
|
||||
"Setting Position on %d to value %f, command: %s",
|
||||
axisNo_, position/MULT, command);
|
||||
|
||||
|
||||
next_poll = -1;
|
||||
|
||||
return status;
|
||||
}
|
||||
/*======================================= Selene Lift Axis Code ===========================================*/
|
||||
@ -776,6 +805,8 @@ asynStatus LiftAxis::move(double position, int relative, double min_velocity,
|
||||
status = pC_->lowLevelWriteRead(axisNo_,command, response);
|
||||
waitStart = 1;
|
||||
|
||||
next_poll = -1;
|
||||
|
||||
return status;
|
||||
}
|
||||
/*--------------------------------------------------------------------------------------------------------
|
||||
@ -786,6 +817,11 @@ asynStatus LiftAxis::poll(bool *moving)
|
||||
{
|
||||
asynStatus status;
|
||||
|
||||
// Protect against excessive polling
|
||||
if(time(NULL) < next_poll){
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
status = getAxisStatus(moving);
|
||||
if(*moving == false && waitStart == 1){
|
||||
*moving = true;
|
||||
@ -795,6 +831,12 @@ asynStatus LiftAxis::poll(bool *moving)
|
||||
if(*moving){
|
||||
waitStart = 0;
|
||||
}
|
||||
|
||||
if(*moving){
|
||||
next_poll = time(NULL) + BUSYPOLL;
|
||||
} else {
|
||||
next_poll = time(NULL) + IDLEPOLL;
|
||||
}
|
||||
callParamCallbacks();
|
||||
return status;
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ class pmacAxis : public SINQAxis
|
||||
{
|
||||
public:
|
||||
/* These are the methods we override from the base class */
|
||||
pmacAxis(pmacController *pController, int axisNo);
|
||||
pmacAxis(pmacController *pController, int axisNo, bool enable=true);
|
||||
virtual ~pmacAxis();
|
||||
asynStatus move(double position, int relative, double min_velocity, double max_velocity, double acceleration);
|
||||
asynStatus moveVelocity(double min_velocity, double max_velocity, double acceleration);
|
||||
@ -62,6 +62,10 @@ class pmacAxis : public SINQAxis
|
||||
int homing;
|
||||
double statusPos;
|
||||
|
||||
time_t next_poll;
|
||||
|
||||
bool autoEnable;
|
||||
|
||||
friend class pmacController;
|
||||
};
|
||||
/*----------------------------------------------------------------------------------------------*/
|
||||
@ -83,12 +87,11 @@ class pmacHRPTAxis : public pmacAxis
|
||||
/*--------------------------------------------------------------------------------------------*/
|
||||
class SeleneAxis : public pmacAxis
|
||||
{
|
||||
|
||||
public:
|
||||
SeleneAxis(SeleneController *pController, int axisNo, double limitTarget);
|
||||
asynStatus move(double position, int relative, double min_velocity, double max_velocity, double acceleration);
|
||||
asynStatus setPosition(double position);
|
||||
asynStatus home(double min_velocity, double max_velocity, double acceleration, int forwards);
|
||||
public:
|
||||
SeleneAxis(SeleneController *pController, int axisNo, double limitTarget);
|
||||
asynStatus move(double position, int relative, double min_velocity, double max_velocity, double acceleration);
|
||||
asynStatus setPosition(double position);
|
||||
asynStatus home(double min_velocity, double max_velocity, double acceleration, int forwards);
|
||||
|
||||
protected:
|
||||
friend class SeleneController;
|
||||
@ -112,6 +115,10 @@ class SeleneAxis : public pmacAxis
|
||||
|
||||
Mark Koennecke, February 2020
|
||||
|
||||
The axis should not be enabled automatically
|
||||
|
||||
Michele Brambilla, February 2020
|
||||
|
||||
*/
|
||||
class LiftAxis : public pmacAxis
|
||||
{
|
||||
|
@ -140,8 +140,8 @@ extern "C" {
|
||||
}
|
||||
|
||||
pmacController::pmacController(const char *portName, const char *lowLevelPortName, int lowLevelPortAddress,
|
||||
int numAxes, double movingPollPeriod, double idlePollPeriod)
|
||||
: SINQController(portName, lowLevelPortName, numAxes+1)
|
||||
int numAxes, double movingPollPeriod, double idlePollPeriod, const int& extraParams)
|
||||
: SINQController(portName, lowLevelPortName, numAxes+1, extraParams)
|
||||
{
|
||||
static const char *functionName = "pmacController::pmacController";
|
||||
|
||||
@ -544,7 +544,20 @@ asynStatus SeleneCreateController(const char *portName, const char *lowLevelPort
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------------------------------------------------*/
|
||||
SeleneController::SeleneController(const char *portName, const char *lowLevelPortName, int lowLevelPortAddress,
|
||||
int numAxes, double movingPollPeriod, double idlePollPeriod) : pmacController(portName,
|
||||
lowLevelPortName,
|
||||
lowLevelPortAddress,
|
||||
numAxes,
|
||||
movingPollPeriod,
|
||||
idlePollPeriod, 3) {
|
||||
static const char *functionName = "seleneController::seleneController";
|
||||
createParam(MotorSetPositionString, asynParamFloat64, &setMotorPosition_);
|
||||
callParamCallbacks();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* C wrapper for the pmacAxis constructor.
|
||||
* See pmacAxis::pmacAxis.
|
||||
@ -711,9 +724,9 @@ asynStatus SeleneController::writeFloat64(asynUser *pasynUser, epicsFloat64 valu
|
||||
/* Set the parameter and readback in the parameter library. */
|
||||
status = pAxis->setDoubleParam(function, value);
|
||||
|
||||
// TODO: somethign is really shitty here: lowLimit and highLimit cannot be on the command
|
||||
if (function == motorLowLimit_) {
|
||||
sprintf(command, "Q%d54=%f", pAxis->axisNo_, (float)value/(float)MULT);
|
||||
// sprintf(command, "Q%d54=%d", pAxis->axisNo_, (int)(value/pAxis->scale_/MULT));
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW,
|
||||
"%s: Setting low limit on controller %s, axis %d to %f\n",
|
||||
functionName, portName, pAxis->axisNo_, value);
|
||||
@ -724,7 +737,13 @@ asynStatus SeleneController::writeFloat64(asynUser *pasynUser, epicsFloat64 valu
|
||||
"%s: Setting high limit on controller %s, axis %d to %f\n",
|
||||
functionName, portName, pAxis->axisNo_, value);
|
||||
errlogPrintf("Setting high limit of axis %d to %f, command = %s\n", pAxis->axisNo_, value, command);
|
||||
}
|
||||
} else if(function == setMotorPosition_){
|
||||
snprintf(command,sizeof(command),"Q%d59=%f", pAxis->axisNo_, value);
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW,
|
||||
"%s: Defining position of axis%d to %f\n",
|
||||
functionName, pAxis->axisNo_, value);
|
||||
errlogPrintf("Defining position of axis %d to %f, command = %s\n", pAxis->axisNo_, value, command);
|
||||
}
|
||||
|
||||
//Execute the command.
|
||||
if (command[0] != 0 && status == asynSuccess) {
|
||||
|
@ -26,8 +26,8 @@
|
||||
class pmacController : public SINQController {
|
||||
|
||||
public:
|
||||
pmacController(const char *portName, const char *lowLevelPortName, int lowLevelPortAddress, int numAxes, double movingPollPeriod,
|
||||
double idlePollPeriod);
|
||||
pmacController(const char *portName, const char *lowLevelPortName, int lowLevelPortAddress, int numAxes, double movingPollPeriod,
|
||||
double idlePollPeriod, const int& extraParams=2);
|
||||
|
||||
virtual ~pmacController();
|
||||
|
||||
@ -143,22 +143,24 @@ class pmacController : public SINQController {
|
||||
|
||||
#define NUM_PMAC_PARAMS (&LAST_PMAC_PARAM - &FIRST_PMAC_PARAM + 1)
|
||||
|
||||
#define MotorSetPositionString "SET_MOTOR_POSITION"
|
||||
|
||||
class SeleneController : public pmacController {
|
||||
public:
|
||||
SeleneController(const char *portName, const char *lowLevelPortName, int lowLevelPortAddress,
|
||||
int numAxes, double movingPollPeriod, double idlePollPeriod) : pmacController(portName,
|
||||
lowLevelPortName,
|
||||
lowLevelPortAddress,
|
||||
numAxes,
|
||||
movingPollPeriod,
|
||||
idlePollPeriod) {};
|
||||
|
||||
SeleneController(const char *portName, const char *lowLevelPortName, int lowLevelPortAddress,
|
||||
int numAxes, double movingPollPeriod, double idlePollPeriod);
|
||||
|
||||
~SeleneController(void) { }
|
||||
|
||||
// overloaded because we have a different command to set the limits
|
||||
asynStatus writeFloat64(asynUser *pasynUser, epicsFloat64 value);
|
||||
|
||||
friend class SeleneAxis;
|
||||
friend class pmacAxis;
|
||||
|
||||
protected:
|
||||
int setMotorPosition_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
88
testEuroMoveUsb.py
Executable file
88
testEuroMoveUsb.py
Executable file
@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: iso-8859-1 -*-
|
||||
"""
|
||||
Created on Thur Feb 06 10:22:42 2020
|
||||
|
||||
@author: gaston emmanuel exil
|
||||
"""
|
||||
|
||||
#pip install pyusb
|
||||
#sudo cp 10-llbDevices.rules /etc/udev/rules.d/
|
||||
#sudo udevadm control --reload-rules && udevadm trigger
|
||||
#reboot
|
||||
|
||||
import sys
|
||||
import usb.core
|
||||
import usb.util
|
||||
from time import sleep
|
||||
|
||||
# 1. Device
|
||||
llbVendorID=0x04B4
|
||||
llbProductID=0x1002
|
||||
|
||||
# 2. Configuration
|
||||
CONFIGURATION_EV3 = 1 # 1-based
|
||||
# 3. Interface
|
||||
INTERFACE_EV3 = 0 # 0-based
|
||||
# 4. Alternate setting
|
||||
SETTING_EV3 = 0 # 0-based
|
||||
# 5. Endpoint
|
||||
ENDPOINT_EV3 = 1 # 0-based
|
||||
|
||||
# find our device
|
||||
device = usb.core.find(idVendor=llbVendorID, idProduct=llbProductID)
|
||||
|
||||
# was it found?
|
||||
if device is None:
|
||||
raise ValueError('Device not found')
|
||||
sys.exit(1)
|
||||
else:
|
||||
if device._manufacturer is None:
|
||||
device._manufacturer = usb.util.get_string(device, device.iManufacturer)
|
||||
print("manufacturer: ", str(device._manufacturer))
|
||||
if device._product is None:
|
||||
device._product = usb.util.get_string(device, device.iProduct)
|
||||
print("product: ", str(device._product))
|
||||
|
||||
|
||||
# By default, the kernel will claim the device and make it available via
|
||||
# /dev/usb/hiddevN and /dev/hidrawN which also prevents us
|
||||
# from communicating otherwise. This removes these kernel devices.
|
||||
# Yes, it is weird to specify an interface before we get to a configuration.
|
||||
|
||||
if device.is_kernel_driver_active(INTERFACE_EV3):
|
||||
print("Detaching kernel driver")
|
||||
device.detach_kernel_driver(INTERFACE_EV3)
|
||||
|
||||
# claim the device
|
||||
usb.util.claim_interface(device, INTERFACE_EV3)
|
||||
|
||||
# write endpoint
|
||||
endpoint_BulkOut = device[0][(0,0)][0]
|
||||
# Read endpoint
|
||||
endpoint_BulkIn = device[0][(0,0)][2]
|
||||
try:
|
||||
command = "L"+chr(13)
|
||||
assert device.write(endpoint_BulkOut, command.encode('utf-8'), 100) == len(command)
|
||||
ret = device.read(endpoint_BulkIn, endpoint_BulkIn.wMaxPacketSize, timeout=30000)
|
||||
print(ret)
|
||||
#byte_str = "".join(chr(n) for n in ret)
|
||||
print(ret.tostring())
|
||||
|
||||
|
||||
|
||||
command = "A1,4"+chr(13)
|
||||
assert device.write(endpoint_BulkOut, command.encode('utf-8'), 100) == len(command)
|
||||
ret = device.read(endpoint_BulkIn, endpoint_BulkIn.wMaxPacketSize, timeout=30000)
|
||||
print(ret)
|
||||
byte_str = "".join(chr(n) for n in ret)
|
||||
print(ret.tostring())
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
# release the device
|
||||
usb.util.release_interface(device, INTERFACE_EV3)
|
||||
# reattach the device to the OS kernel
|
||||
#device.attach_kernel_driver(INTERFACE_EV3)
|
Reference in New Issue
Block a user