Instrument Neutral Distributed Interface INDI  2.0.2
aaf2.cpp
Go to the documentation of this file.
1 /*
2  Arduino ASCOM Focuser 2 (AAF2) INDI Focuser
3 
4  Copyright (C) 2019 Jasem Mutlaq (mutlaqja@ikarustech.com)
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Lesser General Public
8  License as published by the Free Software Foundation; either
9  version 2.1 of the License, or (at your option) any later version.
10 
11  This library is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  Lesser General Public License for more details.
15 
16  You should have received a copy of the GNU Lesser General Public
17  License along with this library; if not, write to the Free Software
18  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 
20 */
21 
22 #include "aaf2.h"
23 
24 #include "indicom.h"
25 
26 #include <cmath>
27 #include <cstring>
28 #include <memory>
29 
30 #include <termios.h>
31 #include <unistd.h>
32 
33 static std::unique_ptr<AAF2> aaf2(new AAF2());
34 
36 {
37  // Absolute, Abort, and Sync
39 
40  setVersion(1, 0);
41 }
42 
44 {
46 
47  // Focuser temperature
48  IUFillNumber(&TemperatureN[0], "TEMPERATURE", "Celsius", "%6.2f", -50, 70., 0., 0.);
49  IUFillNumberVector(&TemperatureNP, TemperatureN, 1, getDeviceName(), "FOCUS_TEMPERATURE", "Temperature",
51 
52  // Relative and absolute movement
53  FocusRelPosN[0].min = 0.;
54  FocusRelPosN[0].max = 50000.;
55  FocusRelPosN[0].value = 0;
56  FocusRelPosN[0].step = 1000;
57 
58  FocusAbsPosN[0].min = 0.;
59  FocusAbsPosN[0].max = 100000.;
60  FocusAbsPosN[0].value = 0;
61  FocusAbsPosN[0].step = 1000;
62 
64 
65  return true;
66 }
67 
69 {
71 
72  if (isConnected())
73  {
74  defineProperty(&TemperatureNP);
75 
76  LOG_INFO("Focuser ready.");
77  }
78  else
79  {
80  deleteProperty(TemperatureNP.name);
81  }
82 
83  return true;
84 }
85 
87 {
88  if (Ack())
89  {
90  LOG_INFO("AAF2 is online.");
91 
92  readVersion();
93 
94  return true;
95  }
96 
97  LOG_INFO("Error retrieving data from AAF2, please ensure AAF2 controller is powered and the port is correct.");
98  return false;
99 }
100 
101 const char * AAF2::getDefaultName()
102 {
103  return "AAF2";
104 }
105 
106 bool AAF2::Ack()
107 {
108  int nbytes_written = 0, nbytes_read = 0, rc = -1;
109  char errstr[MAXRBUF];
110  char resp[5] = {0};
111 
112  tcflush(PortFD, TCIOFLUSH);
113 
114  int numChecks = 0;
115  bool success = false;
116  while (numChecks < 3 && !success)
117  {
118  numChecks++;
119  //wait 1 second between each test.
120  sleep(1);
121 
122  bool transmissionSuccess = (rc = tty_write(PortFD, "#", 1, &nbytes_written)) == TTY_OK;
123  if(!transmissionSuccess)
124  {
125  tty_error_msg(rc, errstr, MAXRBUF);
126  LOGF_ERROR("Handshake Attempt %i, tty transmission error: %s.", numChecks, errstr);
127  }
128 
129  bool responseSuccess = (rc = tty_read(PortFD, resp, 4, DRIVER_TIMEOUT, &nbytes_read)) == TTY_OK;
130  if(!responseSuccess)
131  {
132  tty_error_msg(rc, errstr, MAXRBUF);
133  LOGF_ERROR("Handshake Attempt %i, updatePosition response error: %s.", numChecks, errstr);
134  }
135 
136  success = transmissionSuccess && responseSuccess;
137  }
138 
139  if(!success)
140  {
141  LOG_INFO("Handshake failed after 3 attempts");
142  return false;
143  }
144 
145  tcflush(PortFD, TCIOFLUSH);
146 
147  return !strcmp(resp, "OK!#");
148 }
149 
150 
151 bool AAF2::readTemperature()
152 {
153  char res[DRIVER_RES] = {0};
154 
155  if (sendCommand("C#", res) == false)
156  return false;
157 
158  int32_t temp = 0;
159  int rc = sscanf(res, "C%d:OK", &temp);
160  if (rc > 0)
161  // Hundredth of a degree
162  TemperatureN[0].value = temp / 100.0;
163  else
164  {
165  LOGF_ERROR("Unknown error: focuser temperature value (%s)", res);
166  return false;
167  }
168 
169  return true;
170 }
171 
172 bool AAF2::readVersion()
173 {
174  char res[DRIVER_RES] = {0};
175 
176  if (sendCommand("V#", res) == false)
177  return false;
178 
179  LOGF_INFO("Detected %s", res);
180 
181  return true;
182 }
183 
184 bool AAF2::readPosition()
185 {
186  char res[DRIVER_RES] = {0};
187 
188  if (sendCommand("P#", res) == false)
189  return false;
190 
191  int32_t pos;
192  int rc = sscanf(res, "P%d:OK", &pos);
193 
194  if (rc > 0)
195  FocusAbsPosN[0].value = pos;
196  else
197  {
198  LOGF_ERROR("Unknown error: focuser position value (%s)", res);
199  return false;
200  }
201 
202  return true;
203 }
204 
205 bool AAF2::isMoving()
206 {
207  char res[DRIVER_RES] = {0};
208 
209  if (sendCommand("M#", res) == false)
210  return false;
211 
212  if (strcmp(res, "M1:OK") == 0)
213  return true;
214  else if (strcmp(res, "M0:OK") == 0)
215  return false;
216 
217  LOGF_ERROR("Unknown error: isMoving value (%s)", res);
218  return false;
219 }
220 
221 
222 bool AAF2::SyncFocuser(uint32_t ticks)
223 {
224  char cmd[DRIVER_RES] = {0};
225  snprintf(cmd, DRIVER_RES, "I%d#", ticks);
226  return sendCommand(cmd);
227 }
228 
229 IPState AAF2::MoveAbsFocuser(uint32_t targetTicks)
230 {
231  char cmd[DRIVER_RES] = {0}, res[DRIVER_RES] = {0}, expected[DRIVER_RES] = {0};
232  snprintf(cmd, DRIVER_RES, "T%d#", targetTicks);
233  snprintf(expected, DRIVER_RES, "T%d:OK", targetTicks);
234  if (sendCommand(cmd, res) == false)
235  return IPS_ALERT;
236 
237  targetPos = targetTicks;
238 
239  if (!strcmp(res, expected))
240  return IPS_BUSY;
241  else
242  return IPS_ALERT;
243 }
244 
246 {
247  int32_t newPosition = 0;
248 
249  if (dir == FOCUS_INWARD)
250  newPosition = FocusAbsPosN[0].value - ticks;
251  else
252  newPosition = FocusAbsPosN[0].value + ticks;
253 
254  // Clamp
255  newPosition = std::max(0, std::min(static_cast<int32_t>(FocusAbsPosN[0].max), newPosition));
256  if (MoveAbsFocuser(newPosition) != IPS_BUSY)
257  return IPS_ALERT;
258 
259  FocusRelPosN[0].value = ticks;
261 
262  return IPS_BUSY;
263 }
264 
266 {
267  if (!isConnected())
268  {
270  return;
271  }
272 
273  bool rc = readPosition();
274  if (rc)
275  {
276  if (fabs(lastPos - FocusAbsPosN[0].value) > 5)
277  {
278  IDSetNumber(&FocusAbsPosNP, nullptr);
279  lastPos = FocusAbsPosN[0].value;
280  }
281  }
282 
283  rc = readTemperature();
284  if (rc)
285  {
286  if (fabs(lastTemperature - TemperatureN[0].value) >= 0.5)
287  {
288  IDSetNumber(&TemperatureNP, nullptr);
289  lastTemperature = TemperatureN[0].value;
290  }
291  }
292 
294  {
295  if (!isMoving())
296  {
299  IDSetNumber(&FocusAbsPosNP, nullptr);
300  IDSetNumber(&FocusRelPosNP, nullptr);
301  lastPos = FocusAbsPosN[0].value;
302  LOG_INFO("Focuser reached requested position.");
303  }
304  }
305 
307 }
308 
310 {
311  return sendCommand("H#");
312 }
313 
314 
315 bool AAF2::sendCommand(const char * cmd, char * res)
316 {
317  int nbytes_written = 0, nbytes_read = 0, rc = -1;
318 
319  tcflush(PortFD, TCIOFLUSH);
320 
321  LOGF_DEBUG("CMD <%s>", cmd);
322 
323  if ((rc = tty_write_string(PortFD, cmd, &nbytes_written)) != TTY_OK)
324  {
325  char errstr[MAXRBUF] = {0};
326  tty_error_msg(rc, errstr, MAXRBUF);
327  LOGF_ERROR("Serial write error: %s.", errstr);
328  return false;
329  }
330 
331  if (res == nullptr)
332  return true;
333 
334  if ((rc = tty_nread_section(PortFD, res, DRIVER_RES, DRIVER_DEL, DRIVER_TIMEOUT, &nbytes_read)) != TTY_OK)
335  {
336  char errstr[MAXRBUF] = {0};
337  tty_error_msg(rc, errstr, MAXRBUF);
338  LOGF_ERROR("Serial read error: %s.", errstr);
339  return false;
340  }
341 
342  // Remove the #
343  res[nbytes_read - 1] = 0;
344 
345  LOGF_DEBUG("RES <%s>", res);
346 
347  tcflush(PortFD, TCIOFLUSH);
348 
349  return true;
350 }
Definition: aaf2.h:27
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
Definition: aaf2.cpp:309
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveRelFocuser Move focuser for a relative amount of ticks in a specific direction.
Definition: aaf2.cpp:245
virtual bool SyncFocuser(uint32_t ticks) override
SyncFocuser Set the supplied position as the current focuser position.
Definition: aaf2.cpp:222
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
Definition: aaf2.cpp:265
AAF2()
Definition: aaf2.cpp:35
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: aaf2.cpp:43
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: aaf2.cpp:68
virtual bool Handshake() override
Handshake Try to communicate with Focuser and see if there is a valid response.
Definition: aaf2.cpp:86
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveAbsFocuser Move to an absolute target position.
Definition: aaf2.cpp:229
const char * getDefaultName() override
Definition: aaf2.cpp:101
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
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.
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
void addDebugControl()
Add Debug control to the driver.
INumberVectorProperty FocusAbsPosNP
INumberVectorProperty FocusRelPosNP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
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
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
double max(void)
double min(void)
@ IP_RO
Definition: indiapi.h:184
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
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition: indicom.c:424
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
Definition: indicom.c:482
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition: indicom.c:474
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition: indicom.c:1167
int tty_nread_section(int fd, char *buf, int nsize, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
Definition: indicom.c:666
Implementations for common driver routines.
@ TTY_OK
Definition: indicom.h:150
void IUFillNumberVector(INumberVectorProperty *nvp, INumber *np, int nnp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a number vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:272
void IUFillNumber(INumber *np, const char *name, const char *label, const char *format, double min, double max, double step, double value)
Assign attributes for a number property. The number's auxiliary elements will be set to NULL.
Definition: indidevapi.c:180
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
Definition: indidriver.c:1211
#define LOGF_INFO(fmt,...)
Definition: indilogger.h:82
#define LOGF_DEBUG(fmt,...)
Definition: indilogger.h:83
#define LOGF_ERROR(fmt,...)
Definition: indilogger.h:80
#define LOG_INFO(txt)
Definition: indilogger.h:74
#define MAXRBUF
Definition: indiserver.cpp:102
__u8 cmd[4]
Definition: pwc-ioctl.h:2
char name[MAXINDINAME]
Definition: indiapi.h:323