Instrument Neutral Distributed Interface INDI 2.2.5
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
29static std::unique_ptr<FCUSB> fcusb(new FCUSB());
30static const std::multimap<uint16_t, uint16_t> USBIDs =
31{
32 {0x134A, 0x9023},
33 {0x134A, 0x9024},
34 {0x134A, 0x903F},
35};
36
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
86{
87 return "FCUSB";
88}
89
91{
93
94 FocusSpeedNP[0].setMin(0);
95 FocusSpeedNP[0].setMax(255);
96
97 // PWM Scaler
98 PWMScalerSP[PWM_1_1].fill("PWM_1_1", "1:1", ISS_ON);
99 PWMScalerSP[PWM_1_4].fill("PWM_1_4", "1:4", ISS_OFF);
100 PWMScalerSP[PWM_1_16].fill("PWM_1_16", "1:16", ISS_OFF);
101 PWMScalerSP.fill(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);
120 }
121
122 return true;
123}
124
126{
127 if (!isConnected())
128 return;
129
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 FocusTimerNP[0].setValue(timeleft);
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
154bool 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 (PWMScalerSP.isNameMatch(name))
160 {
161 if (PWMScalerSP.isUpdated(states, names, n))
162 {
163 PWMScalerSP.update(states, names, n);
164 pwmStatus = static_cast<PWMBits>(PWMScalerSP.findOnSwitchIndex());
165 saveConfig(PWMScalerSP);
166 }
167
168 PWMScalerSP.setState(setStatus() ? IPS_OK : IPS_ALERT);
169 PWMScalerSP.apply();
170 return true;
171
172 }
173 }
174
175 return INDI::Focuser::ISNewSwitch(dev, name, states, names, n);
176}
177
178bool FCUSB::getStatus()
179{
180 // 2 bytes response
181 uint8_t status[2] = {0};
182
183 int rc = hid_read(handle, status, 2);
184
185 if (rc < 0)
186 {
187 LOGF_ERROR("getStatus: Error reading from FCUSB to device (%s)", hid_error(handle));
188 return false;
189 }
190
191 LOGF_DEBUG("RES <%#02X %#02X>", status[0], status[1]);
192
193 // Motor Status
194 uint8_t motor = status[0] & 0x3;
195 // 0x2 and 0x3 are identical
196 if (motor == 0x3) motor = 0x2;
197 MotorBits newMotorStatus = static_cast<MotorBits>(motor);
198 if (newMotorStatus != motorStatus)
199 {
200 motorStatus = newMotorStatus;
201 switch (motorStatus)
202 {
203 case MOTOR_OFF:
204 LOG_INFO("Motor is off.");
205 break;
206
207 case MOTOR_REV:
208 LOG_INFO("Motor is moving backwards.");
209 break;
210
211 case MOTOR_FWD:
212 LOG_INFO("Motor is moving forward.");
213 break;
214
215 }
216 }
217
218 uint8_t pwm = (status[0] & 0xC0) >> 6 ;
219 // 0x2 and 0x3 are identical
220 if (pwm == 0x3) pwm = 0x2;
221 PWMBits newPWMStatus = static_cast<PWMBits>(pwm);
222 if (newPWMStatus != pwmStatus)
223 {
224 pwmStatus = newPWMStatus;
225 switch (pwmStatus)
226 {
227 case PWM_1_1:
228 LOG_INFO("PWM Scaler is 1:1");
229 break;
230
231 case PWM_1_4:
232 LOG_INFO("PWM Scaler is 1:4");
233 break;
234
235 case PWM_1_16:
236 LOG_INFO("PWM Scaler is 1:16");
237 break;
238 }
239
240 PWMScalerSP.reset();
241 PWMScalerSP[pwmStatus].setState(ISS_ON);
242 PWMScalerSP.apply();
243 }
244
245 // Update speed (PWM) if it was changed.
246 if (fabs(FocusSpeedNP[0].getValue() - status[1]) > 0)
247 {
248 FocusSpeedNP[0].setValue(status[1]);
249 LOGF_DEBUG("PWM: %d%", FocusSpeedNP[0].getValue());
251 }
252
253 return true;
254}
255
257{
258 motorStatus = MOTOR_OFF;
259
260 LOG_DEBUG("Aborting focuser...");
261
262 bool rc = setStatus();
263
264 if (rc)
265 {
267 {
269 FocusTimerNP[0].setValue(0);
271 }
272
274 {
278 }
279 }
280
281 return rc;
282}
283
284bool FCUSB::stop()
285{
286 motorStatus = MOTOR_OFF;
287
288 LOG_DEBUG("Stopping focuser...");
289
290 bool rc = setStatus();
291
292 if (rc)
293 {
295 {
297 FocusTimerNP[0].setValue(0);
299 }
300
302 {
306 }
307 }
308
309 return rc;
310}
311
313{
314 targetSpeed = speed;
315
316 // Only call send status when the motor is running
317 if (motorStatus != MOTOR_OFF)
318 return setStatus();
319 else
320 return true;
321}
322
323IPState FCUSB::MoveFocuser(FocusDirection dir, int speed, uint16_t duration)
324{
325 FocusDirection targetDirection = dir;
326
327 if (FocusReverseSP[INDI_ENABLED].getState() == ISS_ON)
328 targetDirection = (dir == FOCUS_INWARD) ? FOCUS_OUTWARD : FOCUS_INWARD;
329
330 motorStatus = (targetDirection == FOCUS_INWARD) ? MOTOR_REV : MOTOR_FWD;
331
332 targetSpeed = speed;
333
334 bool rc = setStatus();
335
336 if (!rc)
337 return IPS_ALERT;
338
339 if (duration > 0)
340 {
341 struct timeval duration_secs, current_time;
342 gettimeofday(&current_time, nullptr);
343 duration_secs.tv_sec = duration / 1000;
344 duration_secs.tv_usec = duration % 1000;
345 timeradd(&current_time, &duration_secs, &timedMoveEnd);
346
347 if (duration < getCurrentPollingPeriod())
348 {
349 IEAddTimer(duration, &FCUSB::timedMoveHelper, this);
350 }
351 }
352
353 return IPS_BUSY;
354}
355
356bool FCUSB::setStatus()
357{
358 uint8_t command[2] = {0};
359
360 command[0] |= motorStatus;
361 // Forward (Green) - Reverse (Red)
362 command[0] |= (motorStatus == MOTOR_FWD) ? 0 : FC_LED_RED;
363 // On / Off LED
364 command[0] |= (motorStatus == MOTOR_OFF) ? 0 : FC_LED_ON;
365 // PWM
366 command[0] |= (pwmStatus << 6);
367
368 command[1] = (motorStatus == MOTOR_OFF) ? 0 : static_cast<uint8_t>(targetSpeed);
369
370 LOGF_DEBUG("CMD <%#X %#X>", command[0], command[1]);
371
372 int rc = hid_write(handle, command, 2);
373 if (rc < 0)
374 {
375 LOGF_DEBUG("Setting state failed (%s)", hid_error(handle));
376 return false;
377 }
378
379 return true;
380}
381
383{
384 Focuser::saveConfigItems(fp);
385
386 PWMScalerSP.save(fp);
387
388 return true;
389}
390
391bool FCUSB::ReverseFocuser(bool enabled)
392{
393 INDI_UNUSED(enabled);
394 return true;
395}
396
397void FCUSB::timedMoveHelper(void * context)
398{
399 static_cast<FCUSB *>(context)->timedMoveCallback();
400}
401
402void FCUSB::timedMoveCallback()
403{
404 stop();
405}
Definition fcusb.h:35
virtual bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
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:256
const char * getDefaultName() override
Definition fcusb.cpp:85
PWMBits
Definition fcusb.h:48
@ PWM_1_16
Definition fcusb.h:51
@ PWM_1_1
Definition fcusb.h:49
@ PWM_1_4
Definition fcusb.h:50
MotorBits
Definition fcusb.h:40
@ MOTOR_FWD
Definition fcusb.h:43
@ MOTOR_REV
Definition fcusb.h:42
@ MOTOR_OFF
Definition fcusb.h:41
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:312
virtual bool ReverseFocuser(bool enabled) override
ReverseFocuser Reverse focuser motion direction.
Definition fcusb.cpp:391
virtual bool saveConfigItems(FILE *fp) override
Definition fcusb.cpp:382
static void timedMoveHelper(void *context)
Definition fcusb.cpp:397
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Processes incoming new switch states from the client for this device. Handles updates to FocuserInter...
Definition fcusb.cpp:154
virtual bool updateProperties() override
Updates the state and definition of properties based on the current connection status....
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:323
FCUSB()
Definition fcusb.cpp:37
bool isConnected() const
virtual bool saveConfig(bool silent=false, const char *property=nullptr)
Save the current properties in a configuration file.
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.
INDI::PropertySwitch FocusReverseSP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
INDI::PropertySwitch FocusMotionSP
INDI::PropertyNumber FocusSpeedNP
INDI::PropertyNumber FocusTimerNP
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Processes incoming new switch states from the client for this device. Handles updates to FocuserInter...
virtual bool updateProperties() override
Updates the state and definition of properties based on the current connection status....
virtual bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
void setSupportedConnections(const uint8_t &value)
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
void save(FILE *f) const
save Save property to configuration file.
bool isNameMatch(const char *otherName) const
bool update(const ISState states[], const char *const names[], int n)
bool isUpdated(const ISState states[], const char *const names[], int n) const
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
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:586
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_read(hid_device *dev, unsigned char *data, size_t length)
Read an Input report from a HID device.
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.
HID_API_EXPORT const wchar_t *HID_API_CALL hid_error(hid_device *dev)
Get a string describing the last error which occurred.
int HID_API_EXPORT hid_exit(void)
Finalize the HIDAPI library.
Definition hid_libusb.c:407
ISState
Switch state.
Definition indiapi.h:148
@ ISS_OFF
Definition indiapi.h:149
@ ISS_ON
Definition indiapi.h:150
@ IP_RW
Definition indiapi.h:184
IPState
Property state.
Definition indiapi.h:158
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
@ ISR_1OFMANY
Definition indiapi.h:171
#define INDI_UNUSED(x)
Definition indidevapi.h:132
#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
#define getDeviceName