Instrument Neutral Distributed Interface INDI  2.0.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 static const std::multimap<uint16_t, uint16_t> USBIDs =
31 {
32  {0x134A, 0x9023},
33  {0x134A, 0x9024},
34  {0x134A, 0x903F},
35 };
36 
38 {
39  setVersion(0, 3);
40 
43 }
44 
46 {
47  if (isSimulation())
48  {
50  return true;
51  }
52 
53  // Iterate until the correct VID:PID is found.
54  for (const auto &oneID : USBIDs)
55  {
56  if ( (handle = hid_open(oneID.first, oneID.second, nullptr)) != nullptr)
57  break;
58  }
59 
60  if (handle == nullptr)
61  {
62  LOG_ERROR("No FCUSB focuser found.");
63  return false;
64  }
65 
66  else
67  {
69  }
70 
71  return (handle != nullptr);
72 }
73 
75 {
76  if (isSimulation() == false)
77  {
78  hid_close(handle);
79  hid_exit();
80  }
81 
82  return true;
83 }
84 
85 const char *FCUSB::getDefaultName()
86 {
87  return "FCUSB";
88 }
89 
91 {
93 
94  FocusSpeedN[0].min = 0;
95  FocusSpeedN[0].max = 255;
96 
97  // PWM Scaler
98  IUFillSwitch(&PWMScalerS[0], "PWM_1_1", "1:1", ISS_ON);
99  IUFillSwitch(&PWMScalerS[1], "PWM_1_4", "1:4", ISS_OFF);
100  IUFillSwitch(&PWMScalerS[2], "PWM_1_16", "1:16", ISS_OFF);
101  IUFillSwitchVector(&PWMScalerSP, PWMScalerS, 3, getDeviceName(), "PWM_SCALER", "PWM Scale", OPTIONS_TAB, IP_RW, ISR_1OFMANY,
102  0, IPS_IDLE);
103 
105 
106  return true;
107 }
108 
110 {
112 
113  if (isConnected())
114  {
115  defineProperty(&PWMScalerSP);
116  }
117  else
118  {
119  deleteProperty(PWMScalerSP.name);
120  }
121 
122  return true;
123 }
124 
126 {
127  if (!isConnected())
128  return;
129 
130  if (FocusTimerNP.s == IPS_BUSY)
131  {
132  struct timeval curtime, diff;
133  gettimeofday(&curtime, nullptr);
134  timersub(&timedMoveEnd, &curtime, &diff);
135  int timeleft = diff.tv_sec * 1000 + diff.tv_usec / 1000;
136 
137  if (timeleft < 0)
138  timeleft = 0;
139 
140  FocusTimerN[0].value = timeleft;
141  IDSetNumber(&FocusTimerNP, nullptr);
142 
143  if (timeleft == 0)
144  stop();
145  else if (static_cast<uint32_t>(timeleft) < getCurrentPollingPeriod())
146  {
147  IEAddTimer(timeleft, &FCUSB::timedMoveHelper, this);
148  }
149  }
150 
152 }
153 
154 bool FCUSB::ISNewSwitch(const char * dev, const char * name, ISState * states, char * names[], int n)
155 {
156  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
157  {
158  // Focus Step Mode
159  if (strcmp(PWMScalerSP.name, name) == 0)
160  {
161  IUUpdateSwitch(&PWMScalerSP, states, names, n);
162 
163  pwmStatus = static_cast<PWMBits>(IUFindOnSwitchIndex(&PWMScalerSP));
164 
165  PWMScalerSP.s = setStatus() ? IPS_OK : IPS_ALERT;
166 
167  IDSetSwitch(&PWMScalerSP, nullptr);
168 
169  return true;
170 
171  }
172  }
173 
174  return INDI::Focuser::ISNewSwitch(dev, name, states, names, n);
175 }
176 
177 bool FCUSB::getStatus()
178 {
179  // 2 bytes response
180  uint8_t status[2] = {0};
181 
182  int rc = hid_read(handle, status, 2);
183 
184  if (rc < 0)
185  {
186  LOGF_ERROR("getStatus: Error reading from FCUSB to device (%s)", hid_error(handle));
187  return false;
188  }
189 
190  LOGF_DEBUG("RES <%#02X %#02X>", status[0], status[1]);
191 
192  // Motor Status
193  uint8_t motor = status[0] & 0x3;
194  // 0x2 and 0x3 are identical
195  if (motor == 0x3) motor = 0x2;
196  MotorBits newMotorStatus = static_cast<MotorBits>(motor);
197  if (newMotorStatus != motorStatus)
198  {
199  motorStatus = newMotorStatus;
200  switch (motorStatus)
201  {
202  case MOTOR_OFF:
203  LOG_INFO("Motor is off.");
204  break;
205 
206  case MOTOR_REV:
207  LOG_INFO("Motor is moving backwards.");
208  break;
209 
210  case MOTOR_FWD:
211  LOG_INFO("Motor is moving forward.");
212  break;
213 
214  }
215  }
216 
217  uint8_t pwm = (status[0] & 0xC0) >> 6 ;
218  // 0x2 and 0x3 are identical
219  if (pwm == 0x3) pwm = 0x2;
220  PWMBits newPWMStatus = static_cast<PWMBits>(pwm);
221  if (newPWMStatus != pwmStatus)
222  {
223  pwmStatus = newPWMStatus;
224  switch (pwmStatus)
225  {
226  case PWM_1_1:
227  LOG_INFO("PWM Scaler is 1:1");
228  break;
229 
230  case PWM_1_4:
231  LOG_INFO("PWM Scaler is 1:4");
232  break;
233 
234  case PWM_1_16:
235  LOG_INFO("PWM Scaler is 1:16");
236  break;
237  }
238 
239  IUResetSwitch(&PWMScalerSP);
240  PWMScalerS[pwmStatus].s = ISS_ON;
241  IDSetSwitch(&PWMScalerSP, nullptr);
242  }
243 
244  // Update speed (PWM) if it was changed.
245  if (fabs(FocusSpeedN[0].value - status[1]) > 0)
246  {
247  FocusSpeedN[0].value = status[1];
248  LOGF_DEBUG("PWM: %d%", FocusSpeedN[0].value);
249  IDSetNumber(&FocusSpeedNP, nullptr);
250  }
251 
252  return true;
253 }
254 
256 {
257  motorStatus = MOTOR_OFF;
258 
259  LOG_DEBUG("Aborting focuser...");
260 
261  bool rc = setStatus();
262 
263  if (rc)
264  {
265  if (FocusTimerNP.s != IPS_IDLE)
266  {
268  FocusTimerN[0].value = 0;
269  IDSetNumber(&FocusTimerNP, nullptr);
270  }
271 
272  if (FocusMotionSP.s != IPS_IDLE)
273  {
276  IDSetSwitch(&FocusMotionSP, nullptr);
277  }
278  }
279 
280  return rc;
281 }
282 
283 bool FCUSB::stop()
284 {
285  motorStatus = MOTOR_OFF;
286 
287  LOG_DEBUG("Stopping focuser...");
288 
289  bool rc = setStatus();
290 
291  if (rc)
292  {
293  if (FocusTimerNP.s != IPS_OK)
294  {
296  FocusTimerN[0].value = 0;
297  IDSetNumber(&FocusTimerNP, nullptr);
298  }
299 
300  if (FocusMotionSP.s != IPS_OK)
301  {
304  IDSetSwitch(&FocusMotionSP, nullptr);
305  }
306  }
307 
308  return rc;
309 }
310 
311 bool FCUSB::SetFocuserSpeed(int speed)
312 {
313  targetSpeed = speed;
314 
315  // Only call send status when the motor is running
316  if (motorStatus != MOTOR_OFF)
317  return setStatus();
318  else
319  return true;
320 }
321 
322 IPState FCUSB::MoveFocuser(FocusDirection dir, int speed, uint16_t duration)
323 {
324  FocusDirection targetDirection = dir;
325 
327  targetDirection = (dir == FOCUS_INWARD) ? FOCUS_OUTWARD : FOCUS_INWARD;
328 
329  motorStatus = (targetDirection == FOCUS_INWARD) ? MOTOR_REV : MOTOR_FWD;
330 
331  targetSpeed = speed;
332 
333  bool rc = setStatus();
334 
335  if (!rc)
336  return IPS_ALERT;
337 
338  if (duration > 0)
339  {
340  struct timeval duration_secs, current_time;
341  gettimeofday(&current_time, nullptr);
342  duration_secs.tv_sec = duration / 1000;
343  duration_secs.tv_usec = duration % 1000;
344  timeradd(&current_time, &duration_secs, &timedMoveEnd);
345 
346  if (duration < getCurrentPollingPeriod())
347  {
348  IEAddTimer(duration, &FCUSB::timedMoveHelper, this);
349  }
350  }
351 
352  return IPS_BUSY;
353 }
354 
355 bool FCUSB::setStatus()
356 {
357  uint8_t command[2] = {0};
358 
359  command[0] |= motorStatus;
360  // Forward (Green) - Reverse (Red)
361  command[0] |= (motorStatus == MOTOR_FWD) ? 0 : FC_LED_RED;
362  // On / Off LED
363  command[0] |= (motorStatus == MOTOR_OFF) ? 0 : FC_LED_ON;
364  // PWM
365  command[0] |= (pwmStatus << 6);
366 
367  command[1] = (motorStatus == MOTOR_OFF) ? 0 : static_cast<uint8_t>(targetSpeed);
368 
369  LOGF_DEBUG("CMD <%#X %#X>", command[0], command[1]);
370 
371  int rc = hid_write(handle, command, 2);
372  if (rc < 0)
373  {
374  LOGF_DEBUG("Setting state failed (%s)", hid_error(handle));
375  return false;
376  }
377 
378  return true;
379 }
380 
381 bool FCUSB::saveConfigItems(FILE * fp)
382 {
383  Focuser::saveConfigItems(fp);
384 
385  IUSaveConfigSwitch(fp, &PWMScalerSP);
386 
387  return true;
388 }
389 
390 bool FCUSB::ReverseFocuser(bool enabled)
391 {
392  INDI_UNUSED(enabled);
393  return true;
394 }
395 
396 void FCUSB::timedMoveHelper(void * context)
397 {
398  static_cast<FCUSB *>(context)->timedMoveCallback();
399 }
400 
401 void FCUSB::timedMoveCallback()
402 {
403  stop();
404 }
Definition: fcusb.h:29
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: fcusb.cpp:90
virtual bool Disconnect() override
Disconnect from device.
Definition: fcusb.cpp:74
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
Definition: fcusb.cpp:255
const char * getDefaultName() override
Definition: fcusb.cpp:85
PWMBits
Definition: fcusb.h:42
@ PWM_1_16
Definition: fcusb.h:45
@ PWM_1_1
Definition: fcusb.h:43
@ PWM_1_4
Definition: fcusb.h:44
MotorBits
Definition: fcusb.h:34
@ MOTOR_FWD
Definition: fcusb.h:37
@ MOTOR_REV
Definition: fcusb.h:36
@ MOTOR_OFF
Definition: fcusb.h:35
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
Definition: fcusb.cpp:125
virtual bool Connect() override
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
Definition: fcusb.cpp:45
virtual bool SetFocuserSpeed(int speed) override
SetFocuserSpeed Set Focuser speed.
Definition: fcusb.cpp:311
virtual bool ReverseFocuser(bool enabled) override
ReverseFocuser Reverse focuser motion direction.
Definition: fcusb.cpp:390
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Focuser Presets in the configuration file
Definition: fcusb.cpp:381
static void timedMoveHelper(void *context)
Definition: fcusb.cpp:396
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition: fcusb.cpp:154
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: fcusb.cpp:109
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:322
FCUSB()
Definition: fcusb.cpp:37
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
void addSimulationControl()
Add Simulation control to the driver.
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
bool isSimulation() const
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
ISwitchVectorProperty FocusMotionSP
INumberVectorProperty FocusSpeedNP
INumberVectorProperty FocusTimerNP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: indifocuser.cpp:42
void setSupportedConnections(const uint8_t &value)
setConnection Set Focuser connection mode. Child class should call this in the constructor before Foc...
const char * OPTIONS_TAB
OPTIONS_TAB Where all the driver's options are located. Those may include auxiliary controls,...
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:582
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
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
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
void HID_API_EXPORT hid_close(hid_device *dev)
Close a HID device.
Definition: hid_libusb.c:1163
HID_API_EXPORT const wchar_t *HID_API_CALL hid_error(hid_device *dev)
Get a string describing the last error which occurred.
Definition: hid_libusb.c:1227
int HID_API_EXPORT hid_exit(void)
Finalize the HIDAPI library.
Definition: hid_libusb.c:407
ISState
Switch state.
Definition: indiapi.h:150
@ ISS_OFF
Definition: indiapi.h:151
@ ISS_ON
Definition: indiapi.h:152
@ IP_RW
Definition: indiapi.h:186
IPState
Property state.
Definition: indiapi.h:160
@ IPS_BUSY
Definition: indiapi.h:163
@ IPS_ALERT
Definition: indiapi.h:164
@ IPS_IDLE
Definition: indiapi.h:161
@ IPS_OK
Definition: indiapi.h:162
@ ISR_1OFMANY
Definition: indiapi.h:173
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition: indidevapi.c:25
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition: indidevapi.c:128
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition: indidevapi.c:148
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: indidevapi.c:158
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: indidevapi.c:235
#define INDI_UNUSED(x)
Definition: indidevapi.h:131
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
Definition: indidriver.c:1308
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
Definition: indidriver.c:1211
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
Definition: indidriver.c:1231
#define LOG_DEBUG(txt)
Definition: indilogger.h:75
#define LOGF_DEBUG(fmt,...)
Definition: indilogger.h:83
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition: indilogger.h:72
#define LOGF_ERROR(fmt,...)
Definition: indilogger.h:80
#define LOG_INFO(txt)
Definition: indilogger.h:74
char name[MAXINDINAME]
Definition: indiapi.h:371