Instrument Neutral Distributed Interface INDI  1.9.2
fcusb.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  Copyright(c) 2019 Jasem Mutlaq. All rights reserved.
3 
4  Shoestring FCUSB Focuser
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Library General Public
8  License version 2 as published by the Free Software Foundation.
9  .
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Library General Public License for more details.
14  .
15  You should have received a copy of the GNU Library General Public License
16  along with this library; see the file COPYING.LIB. If not, write to
17  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  Boston, MA 02110-1301, USA.
19 *******************************************************************************/
20 
21 #include "fcusb.h"
22 
23 #include <cmath>
24 #include <cstring>
25 #include <memory>
26 
27 #define FOCUS_SETTINGS_TAB "Settings"
28 
29 static std::unique_ptr<FCUSB> fcusb(new FCUSB());
30 
32 {
33  setVersion(0, 2);
34 
37 }
38 
40 {
41  if (isSimulation())
42  {
44  return true;
45  }
46 
47  handle = hid_open(0x134A, 0x9023, nullptr);
48 
49  if (handle == nullptr)
50  {
51  handle = hid_open(0x134A, 0x9024, nullptr);
52 
53  if (handle == nullptr)
54  {
55  LOG_ERROR("No FCUSB focuser found.");
56  return false;
57  }
58  }
59  else
60  {
62  }
63 
64  return (handle != nullptr);
65 }
66 
68 {
69  if (isSimulation() == false)
70  {
71  hid_close(handle);
72  hid_exit();
73  }
74 
75  return true;
76 }
77 
78 const char *FCUSB::getDefaultName()
79 {
80  return "FCUSB";
81 }
82 
84 {
86 
87  FocusSpeedN[0].min = 0;
88  FocusSpeedN[0].max = 255;
89 
90  // PWM Scaler
91  IUFillSwitch(&PWMScalerS[0], "PWM_1_1", "1:1", ISS_ON);
92  IUFillSwitch(&PWMScalerS[1], "PWM_1_4", "1:4", ISS_OFF);
93  IUFillSwitch(&PWMScalerS[2], "PWM_1_16", "1:16", ISS_OFF);
94  IUFillSwitchVector(&PWMScalerSP, PWMScalerS, 3, getDeviceName(), "PWM_SCALER", "PWM Scale", OPTIONS_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
95 
97 
98  return true;
99 }
100 
102 {
104 
105  if (isConnected())
106  {
107  defineProperty(&PWMScalerSP);
108  }
109  else
110  {
111  deleteProperty(PWMScalerSP.name);
112  }
113 
114  return true;
115 }
116 
118 {
119  if (!isConnected())
120  return;
121 
122  if (FocusTimerNP.s == IPS_BUSY)
123  {
124  struct timeval curtime, diff;
125  gettimeofday(&curtime, nullptr);
126  timersub(&timedMoveEnd, &curtime, &diff);
127  int timeleft = diff.tv_sec * 1000 + diff.tv_usec / 1000;
128 
129  if (timeleft < 0)
130  timeleft = 0;
131 
132  FocusTimerN[0].value = timeleft;
133  IDSetNumber(&FocusTimerNP, nullptr);
134 
135  if (timeleft == 0)
136  stop();
137  else if (static_cast<uint32_t>(timeleft) < getCurrentPollingPeriod())
138  {
139  IEAddTimer(timeleft, &FCUSB::timedMoveHelper, this);
140  }
141  }
142 
144 }
145 
146 bool FCUSB::ISNewSwitch(const char * dev, const char * name, ISState * states, char * names[], int n)
147 {
148  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
149  {
150  // Focus Step Mode
151  if (strcmp(PWMScalerSP.name, name) == 0)
152  {
153  IUUpdateSwitch(&PWMScalerSP, states, names, n);
154 
155  pwmStatus = static_cast<PWMBits>(IUFindOnSwitchIndex(&PWMScalerSP));
156 
157  PWMScalerSP.s = setStatus() ? IPS_OK : IPS_ALERT;
158 
159  IDSetSwitch(&PWMScalerSP, nullptr);
160 
161  return true;
162 
163  }
164  }
165 
166  return INDI::Focuser::ISNewSwitch(dev, name, states, names, n);
167 }
168 
169 bool FCUSB::getStatus()
170 {
171  // 2 bytes response
172  uint8_t status[2] = {0};
173 
174  int rc = hid_read(handle, status, 2);
175 
176  if (rc < 0)
177  {
178  LOGF_ERROR("getStatus: Error reading from FCUSB to device (%s)", hid_error(handle));
179  return false;
180  }
181 
182  LOGF_DEBUG("RES <%#02X %#02X>", status[0], status[1]);
183 
184  // Motor Status
185  uint8_t motor = status[0] & 0x3;
186  // 0x2 and 0x3 are identical
187  if (motor == 0x3) motor = 0x2;
188  MotorBits newMotorStatus = static_cast<MotorBits>(motor);
189  if (newMotorStatus != motorStatus)
190  {
191  motorStatus = newMotorStatus;
192  switch (motorStatus)
193  {
194  case MOTOR_OFF:
195  LOG_INFO("Motor is off.");
196  break;
197 
198  case MOTOR_REV:
199  LOG_INFO("Motor is moving backwards.");
200  break;
201 
202  case MOTOR_FWD:
203  LOG_INFO("Motor is moving forward.");
204  break;
205 
206  }
207  }
208 
209  uint8_t pwm = (status[0] & 0xC0) >> 6 ;
210  // 0x2 and 0x3 are identical
211  if (pwm == 0x3) pwm = 0x2;
212  PWMBits newPWMStatus = static_cast<PWMBits>(pwm);
213  if (newPWMStatus != pwmStatus)
214  {
215  pwmStatus = newPWMStatus;
216  switch (pwmStatus)
217  {
218  case PWM_1_1:
219  LOG_INFO("PWM Scaler is 1:1");
220  break;
221 
222  case PWM_1_4:
223  LOG_INFO("PWM Scaler is 1:4");
224  break;
225 
226  case PWM_1_16:
227  LOG_INFO("PWM Scaler is 1:16");
228  break;
229  }
230 
231  IUResetSwitch(&PWMScalerSP);
232  PWMScalerS[pwmStatus].s = ISS_ON;
233  IDSetSwitch(&PWMScalerSP, nullptr);
234  }
235 
236  // Update speed (PWM) if it was changed.
237  if (fabs(FocusSpeedN[0].value - status[1]) > 0)
238  {
239  FocusSpeedN[0].value = status[1];
240  LOGF_DEBUG("PWM: %d%", FocusSpeedN[0].value);
241  IDSetNumber(&FocusSpeedNP, nullptr);
242  }
243 
244  return true;
245 }
246 
248 {
249  motorStatus = MOTOR_OFF;
250 
251  LOG_DEBUG("Aborting focuser...");
252 
253  bool rc = setStatus();
254 
255  if (rc)
256  {
257  if (FocusTimerNP.s != IPS_IDLE)
258  {
260  FocusTimerN[0].value = 0;
261  IDSetNumber(&FocusTimerNP, nullptr);
262  }
263 
264  if (FocusMotionSP.s != IPS_IDLE)
265  {
268  IDSetSwitch(&FocusMotionSP, nullptr);
269  }
270  }
271 
272  return rc;
273 }
274 
275 bool FCUSB::stop()
276 {
277  motorStatus = MOTOR_OFF;
278 
279  LOG_DEBUG("Stopping focuser...");
280 
281  bool rc = setStatus();
282 
283  if (rc)
284  {
285  if (FocusTimerNP.s != IPS_OK)
286  {
288  FocusTimerN[0].value = 0;
289  IDSetNumber(&FocusTimerNP, nullptr);
290  }
291 
292  if (FocusMotionSP.s != IPS_OK)
293  {
296  IDSetSwitch(&FocusMotionSP, nullptr);
297  }
298  }
299 
300  return rc;
301 }
302 
303 bool FCUSB::SetFocuserSpeed(int speed)
304 {
305  targetSpeed = speed;
306 
307  // Only call send status when the motor is running
308  if (motorStatus != MOTOR_OFF)
309  return setStatus();
310  else
311  return true;
312 }
313 
314 IPState FCUSB::MoveFocuser(FocusDirection dir, int speed, uint16_t duration)
315 {
316  FocusDirection targetDirection = dir;
317 
319  targetDirection = (dir == FOCUS_INWARD) ? FOCUS_OUTWARD : FOCUS_INWARD;
320 
321  motorStatus = (targetDirection == FOCUS_INWARD) ? MOTOR_REV : MOTOR_FWD;
322 
323  targetSpeed = speed;
324 
325  bool rc = setStatus();
326 
327  if (!rc)
328  return IPS_ALERT;
329 
330  if (duration > 0)
331  {
332  struct timeval duration_secs, current_time;
333  gettimeofday(&current_time, nullptr);
334  duration_secs.tv_sec = duration / 1000;
335  duration_secs.tv_usec = duration % 1000;
336  timeradd(&current_time, &duration_secs, &timedMoveEnd);
337 
338  if (duration < getCurrentPollingPeriod())
339  {
340  IEAddTimer(duration, &FCUSB::timedMoveHelper, this);
341  }
342  }
343 
344  return IPS_BUSY;
345 }
346 
347 bool FCUSB::setStatus()
348 {
349  uint8_t command[2] = {0};
350 
351  command[0] |= motorStatus;
352  // Forward (Green) - Reverse (Red)
353  command[0] |= (motorStatus == MOTOR_FWD) ? 0 : FC_LED_RED;
354  // On / Off LED
355  command[0] |= (motorStatus == MOTOR_OFF) ? 0 : FC_LED_ON;
356  // PWM
357  command[0] |= (pwmStatus << 6);
358 
359  command[1] = (motorStatus == MOTOR_OFF) ? 0 : static_cast<uint8_t>(targetSpeed);
360 
361  LOGF_DEBUG("CMD <%#X %#X>", command[0], command[1]);
362 
363  int rc = hid_write(handle, command, 2);
364  if (rc < 0)
365  {
366  LOGF_DEBUG("Setting state failed (%s)", hid_error(handle));
367  return false;
368  }
369 
370  return true;
371 }
372 
373 bool FCUSB::saveConfigItems(FILE * fp)
374 {
375  Focuser::saveConfigItems(fp);
376 
377  IUSaveConfigSwitch(fp, &PWMScalerSP);
378 
379  return true;
380 }
381 
382 bool FCUSB::ReverseFocuser(bool enabled)
383 {
384  INDI_UNUSED(enabled);
385  return true;
386 }
387 
388 void FCUSB::timedMoveHelper(void * context)
389 {
390  static_cast<FCUSB *>(context)->timedMoveCallback();
391 }
392 
393 void FCUSB::timedMoveCallback()
394 {
395  stop();
396 }
fcusb.h
FCUSB::FCUSB
FCUSB()
Definition: fcusb.cpp:31
FCUSB::getDefaultName
const char * getDefaultName() override
Definition: fcusb.cpp:78
IPState
IPState
Property state.
Definition: indiapi.h:158
hid_write
int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length)
Write an Output report to a HID device.
Definition: hid_libusb.c:929
LOGF_ERROR
#define LOGF_ERROR(fmt,...)
Definition: indilogger.h:80
IPS_OK
@ IPS_OK
Definition: indiapi.h:161
FCUSB::PWM_1_4
@ PWM_1_4
Definition: fcusb.h:62
_INumberVectorProperty::s
IPState s
Definition: indiapi.h:332
ISS_OFF
@ ISS_OFF
Definition: indiapi.h:150
IPS_ALERT
@ IPS_ALERT
Definition: indiapi.h:163
INDI::DefaultDevice::isSimulation
bool isSimulation() const
Definition: defaultdevice.cpp:734
INDI::DefaultDevice::defineProperty
void defineProperty(INumberVectorProperty *property)
Definition: defaultdevice.cpp:997
OPTIONS_TAB
const char * OPTIONS_TAB
OPTIONS_TAB Where all the driver's options are located. Those may include auxiliary controls,...
Definition: defaultdevice.cpp:39
hid_read
int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length)
Read an Input report from a HID device.
Definition: hid_libusb.c:1095
INDI::FocuserInterface::FocusTimerN
INumber FocusTimerN[1]
Definition: indifocuserinterface.h:279
hid_exit
int HID_API_EXPORT hid_exit(void)
Finalize the HIDAPI library.
Definition: hid_libusb.c:407
INDI::FocuserInterface::FOCUSER_HAS_VARIABLE_SPEED
@ FOCUSER_HAS_VARIABLE_SPEED
Definition: indifocuserinterface.h:79
INDI_UNUSED
#define INDI_UNUSED(x)
Definition: indidevapi.h:799
INDI::FocuserInterface::FOCUSER_CAN_SYNC
@ FOCUSER_CAN_SYNC
Definition: indifocuserinterface.h:78
INDI::DefaultDevice::setVersion
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
Definition: defaultdevice.cpp:1219
INDI::BaseDevice::getDeviceName
const char * getDeviceName() const
Definition: basedevice.cpp:799
FCUSB::timedMoveHelper
static void timedMoveHelper(void *context)
Definition: fcusb.cpp:388
FCUSB::ReverseFocuser
virtual bool ReverseFocuser(bool enabled) override
ReverseFocuser Reverse focuser motion direction.
Definition: fcusb.cpp:382
LOG_INFO
#define LOG_INFO(txt)
Definition: indilogger.h:74
IUResetSwitch
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition: indicom.c:1421
INDI::DefaultDevice::getCurrentPollingPeriod
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
Definition: defaultdevice.cpp:1139
IEAddTimer
int IEAddTimer(int millisecs, IE_TCF *fp, void *p)
Register a new single-shot timer function, fp, to be called with ud as argument after ms.
Definition: eventloop.c:525
LOGF_DEBUG
#define LOGF_DEBUG(fmt,...)
Definition: indilogger.h:83
INDI::DefaultDevice::SetTimer
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
Definition: defaultdevice.cpp:865
FCUSB
Definition: fcusb.h:28
INDI::FocuserInterface::FocusTimerNP
INumberVectorProperty FocusTimerNP
Definition: indifocuserinterface.h:278
FCUSB::MoveFocuser
virtual IPState MoveFocuser(FocusDirection dir, int speed, uint16_t duration) override
MoveFocuser the focuser in a particular direction with a specific speed for a finite duration.
Definition: fcusb.cpp:314
INDI::FocuserInterface::FOCUS_INWARD
@ FOCUS_INWARD
Definition: indifocuserinterface.h:68
FCUSB::Disconnect
virtual bool Disconnect() override
Disconnect from device.
Definition: fcusb.cpp:67
IUFillSwitchVector
void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char *dev, const char *name, const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
Assign attributes for a switch vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidriver.c:412
FCUSB::MOTOR_FWD
@ MOTOR_FWD
Definition: fcusb.h:73
IPS_BUSY
@ IPS_BUSY
Definition: indiapi.h:162
ISR_1OFMANY
@ ISR_1OFMANY
Definition: indiapi.h:172
IPS_IDLE
@ IPS_IDLE
Definition: indiapi.h:160
INDI::FocuserInterface::FocusMotionSP
ISwitchVectorProperty FocusMotionSP
Definition: indifocuserinterface.h:274
FCUSB::MOTOR_OFF
@ MOTOR_OFF
Definition: fcusb.h:71
FCUSB::PWMBits
PWMBits
Definition: fcusb.h:59
hid_error
const HID_API_EXPORT wchar_t *HID_API_CALL hid_error(hid_device *dev)
Get a string describing the last error which occurred.
Definition: hid_libusb.c:1227
INDI::FocuserInterface::FOCUSER_CAN_REVERSE
@ FOCUSER_CAN_REVERSE
Definition: indifocuserinterface.h:77
INDI::Focuser::updateProperties
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: indifocuser.cpp:120
IUUpdateSwitch
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
Definition: indidriver.c:171
FCUSB::initProperties
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: fcusb.cpp:83
INDI::BaseDevice::isConnected
bool isConnected() const
Definition: basedevice.cpp:518
LOG_DEBUG
#define LOG_DEBUG(txt)
Definition: indilogger.h:75
INDI::Focuser::CONNECTION_NONE
@ CONNECTION_NONE
Definition: indifocuser.h:54
LOG_ERROR
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition: indilogger.h:72
INDI::FocuserInterface::SetCapability
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
Definition: indifocuserinterface.h:95
FCUSB::saveConfigItems
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Focuser Presets in the configuration file
Definition: fcusb.cpp:373
INDI::BaseDevice::INDI_ENABLED
@ INDI_ENABLED
Definition: basedevice.h:64
FCUSB::updateProperties
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: fcusb.cpp:101
FCUSB::MOTOR_REV
@ MOTOR_REV
Definition: fcusb.h:72
FCUSB::PWM_1_16
@ PWM_1_16
Definition: fcusb.h:63
_ISwitchVectorProperty::s
IPState s
Definition: indiapi.h:382
INDI::FocuserInterface::FocusReverseS
ISwitch FocusReverseS[2]
Definition: indifocuserinterface.h:303
INDI::FocuserInterface::FOCUSER_CAN_ABORT
@ FOCUSER_CAN_ABORT
Definition: indifocuserinterface.h:76
FCUSB::ISNewSwitch
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition: fcusb.cpp:146
FCUSB::PWM_1_1
@ PWM_1_1
Definition: fcusb.h:61
INDI::FocuserInterface::FocusDirection
FocusDirection
Definition: indifocuserinterface.h:66
IP_RW
@ IP_RW
Definition: indiapi.h:185
INDI::FocuserInterface::FocusSpeedNP
INumberVectorProperty FocusSpeedNP
Definition: indifocuserinterface.h:268
ISState
ISState
Switch state.
Definition: indiapi.h:148
INDI::Focuser::setSupportedConnections
void setSupportedConnections(const uint8_t &value)
setConnection Set Focuser connection mode. Child class should call this in the constructor before Foc...
Definition: indifocuser.cpp:373
FCUSB::TimerHit
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
Definition: fcusb.cpp:117
FCUSB::AbortFocuser
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
Definition: fcusb.cpp:247
IUFindOnSwitchIndex
int IUFindOnSwitchIndex(const ISwitchVectorProperty *sp)
Returns the index of first ON switch it finds in the vector switch property.
Definition: indicom.c:1403
INDI::DefaultDevice::addSimulationControl
void addSimulationControl()
Add Simulation control to the driver.
Definition: defaultdevice.cpp:646
hid_open
hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
Open a HID device using a Vendor ID (VID), Product ID (PID) and optionally a serial number.
Definition: hid_libusb.c:612
IUSaveConfigSwitch
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition: indicom.c:1444
FCUSB::MotorBits
MotorBits
Definition: fcusb.h:51
INDI::Focuser::initProperties
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: indifocuser.cpp:58
INDI::DefaultDevice::deleteProperty
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
Definition: defaultdevice.cpp:965
FCUSB::SetFocuserSpeed
virtual bool SetFocuserSpeed(int speed) override
SetFocuserSpeed Set Focuser speed.
Definition: fcusb.cpp:303
INDI::Focuser::ISNewSwitch
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition: indifocuser.cpp:168
hid_close
void HID_API_EXPORT hid_close(hid_device *dev)
Close a HID device.
Definition: hid_libusb.c:1163
IDSetNumber
void void void IDSetNumber(const INumberVectorProperty *n, const char *msg,...) ATTRIBUTE_FORMAT_PRINTF(2
Tell client to update an existing number vector property.
IDSetSwitch
void void void void void IDSetSwitch(const ISwitchVectorProperty *s, const char *msg,...) ATTRIBUTE_FORMAT_PRINTF(2
Tell client to update an existing switch vector property.
FCUSB::Connect
virtual bool Connect() override
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
Definition: fcusb.cpp:39
IUFillSwitch
void IUFillSwitch(ISwitch *sp, const char *name, const char *label, ISState s)
Assign attributes for a switch property. The switch's auxiliary elements will be set to NULL.
Definition: indidriver.c:320
INDI::FocuserInterface::FocusSpeedN
INumber FocusSpeedN[1]
Definition: indifocuserinterface.h:269
_ISwitchVectorProperty::name
char name[MAXINDINAME]
Definition: indiapi.h:370
ISS_ON
@ ISS_ON
Definition: indiapi.h:151
INDI::FocuserInterface::FOCUS_OUTWARD
@ FOCUS_OUTWARD
Definition: indifocuserinterface.h:69