31#define NIGHTCRAWLER_TIMEOUT 3
32#define NIGHTCRAWLER_THRESHOLD 0.1
34#define NC_25_STEPS 374920
35#define NC_30_STEPS 444080
36#define NC_35_STEPS 505960
38#define ROTATOR_TAB "Rotator"
40#define SETTINGS_TAB "Settings"
45static std::unique_ptr<NightCrawler> tommyGoodBoy(
new NightCrawler());
65 SyncFocusNP[0].
fill(
"FOCUS_SYNC_OFFSET",
"Ticks",
"%.f", 0, 100000., 0., 0.);
70 VoltageNP[0].
fill(
"VALUE",
"Value (v)",
"%.2f", 0, 30., 1., 0.);
74 TemperatureNP[0].
fill(
"TEMPERATURE",
"Value (C)",
"%.2f", -100, 100., 1., 0.);
79 IUFillNumber(&TemperatureOffsetN[0],
"OFFSET",
"Offset",
"%.2f", -15, 15., 1., 0.);
84 FocusStepDelayNP[0].
fill(
"FOCUS_STEP",
"Value",
"%.f", 7, 100., 1., 7.);
89 LimitSwitchLP[ROTATION_SWITCH].
fill(
"ROTATION_SWITCH",
"Rotation Home",
IPS_OK);
90 LimitSwitchLP[OUT_SWITCH].
fill(
"OUT_SWITCH",
"Focus Out Limit",
IPS_OK);
91 LimitSwitchLP[IN_SWITCH].
fill(
"IN_SWITCH",
"Focus In Limit",
IPS_OK);
114 BrightnessNP[BRIGHTNESS_DISPLAY].
fill(
"BRIGHTNESS_DISPLAY",
"Display",
"%.f", 0, 255., 10., 150.);
115 BrightnessNP[BRIGHTNESS_SLEEP].
fill(
"BRIGHTNESS_SLEEP",
"Sleep",
"%.f", 1, 255., 10., 16.);
126 RotatorAbsPosNP[0].
fill(
"ROTATOR_ABSOLUTE_POSITION",
"Ticks",
"%.f", 0., 100000., 1000., 0.);
131 RotatorStepDelayNP[0].
fill(
"ROTATOR_STEP",
"Value",
"%.f", 7, 100., 1., 7.);
136 CustomRotatorStepNP[0].
fill(
"STEPS",
"Steps",
"%.f", 0, 5000000, 0, 0);
144 GotoAuxNP[0].
fill(
"AUX_ABSOLUTE_POSITION",
"Ticks",
"%.f", 0, 100000., 0., 0.);
153 SyncAuxNP[0].
fill(
"AUX_SYNC_TICK",
"Ticks",
"%.f", 0, 100000., 0., 0.);
157 AuxStepDelayNP[0].
fill(
"AUX_STEP",
"Value",
"%.f", 7, 100., 1., 7.);
248 LOG_INFO(
"Error retrieving data from NightCrawler, please ensure NightCrawler controller is powered and the port is correct.");
254 return "NightCrawler";
257bool NightCrawler::Ack()
259 bool rcFirmware = getFirmware();
260 bool rcType = getFocuserType();
262 return (rcFirmware && rcType);
265bool NightCrawler::getFirmware()
267 int nbytes_written = 0, nbytes_read = 0, rc = -1;
271 tcflush(
PortFD, TCIOFLUSH);
287 tcflush(
PortFD, TCIOFLUSH);
289 resp[nbytes_read - 1] =
'\0';
296bool NightCrawler::getFocuserType()
298 int nbytes_written = 0, nbytes_read = 0, rc = -1;
302 tcflush(
PortFD, TCIOFLUSH);
318 tcflush(
PortFD, TCIOFLUSH);
320 resp[nbytes_read - 1] =
'\0';
324 if (strcmp(resp,
"2.5 NC") == 0)
330 else if (strcmp(resp,
"3.0 NC") == 0)
343 m_RotatorTicksPerDegree = m_RotatorStepsPerRevolution / 360.0;
348bool NightCrawler::gotoMotor(MotorType
type, int32_t position)
353 int nbytes_written = 0, nbytes_read = 0, rc = -1;
356 snprintf(
cmd, 16,
"%dSN %d#",
type + 1, position);
360 tcflush(
PortFD, TCIOFLUSH);
372 LOGF_ERROR(
"%s error: %s.", __FUNCTION__, errstr);
378 return startMotor(
type);
381bool NightCrawler::getPosition(MotorType
type)
387 int nbytes_written = 0, nbytes_read = 0, rc = -1;
390 snprintf(
cmd, 16,
"%dGP#",
type + 1);
394 tcflush(
PortFD, TCIOFLUSH);
401 abnormalDisconnect();
408 LOGF_ERROR(
"%s error: %s.", __FUNCTION__, errstr);
412 res[nbytes_read] =
'\0';
416 position = atoi(res);
418 if (position != -1e6)
423 RotatorAbsPosNP[0].setValue(position);
425 GotoAuxNP[0].setValue(position);
444void NightCrawler::abnormalDisconnect()
464 bool atLeastOne =
false;
466 for (
int i = 0; i < n; i++)
478 LOG_ERROR(
"At least one selection must be on.");
479 HomeSelectionSP.
apply();
483 HomeSelectionSP.
update(states, names, n);
485 HomeSelectionSP.
apply();
490 uint8_t selection = 0;
499 if (findHome(selection))
503 LOG_WARN(
"Homing process can take up to 10 minutes. You cannot control the unit until the process is fully complete.");
509 LOG_ERROR(
"Failed to start homing process.");
517 EncoderSP.
update(states, names, n);
520 LOGF_INFO(
"Encoders are %s", (EncoderSP[0].getState() ==
ISS_ON) ?
"ON" :
"OFF");
538 else if (strstr(name,
"ROTATOR"))
554 bool rc = syncMotor(
MOTOR_FOCUS,
static_cast<uint32_t
>(values[0]));
557 SyncFocusNP[0].setValue(values[0]);
564 bool rc = syncMotor(
MOTOR_AUX,
static_cast<uint32_t
>(values[0]));
567 SyncAuxNP[0].setValue(values[0]);
572 else if (strcmp(name, TemperatureOffsetNP.
name) == 0)
574 bool rc = setTemperatureOffset(values[0]);
581 bool rc = setStepDelay(
MOTOR_FOCUS,
static_cast<uint32_t
>(values[0]));
584 FocusStepDelayNP[0].setValue(values[0]);
585 FocusStepDelayNP.
apply();
590 bool rc = setStepDelay(
MOTOR_ROTATOR,
static_cast<uint32_t
>(values[0]));
593 RotatorStepDelayNP[0].setValue(values[0]);
594 RotatorStepDelayNP.
apply();
599 bool rc = setStepDelay(
MOTOR_AUX,
static_cast<uint32_t
>(values[0]));
602 AuxStepDelayNP[0].setValue(values[0]);
603 AuxStepDelayNP.
apply();
608 BrightnessNP.
update(values, names, n);
609 bool rcDisplay = setDisplayBrightness(
static_cast<uint8_t
>(BrightnessNP[BRIGHTNESS_DISPLAY].getValue()));
610 bool rcSleep = setSleepBrightness(
static_cast<uint8_t
>(BrightnessNP[BRIGHTNESS_SLEEP].getValue()));
611 if (rcDisplay && rcSleep)
616 BrightnessNP.
apply();
621 bool rc = gotoMotor(
MOTOR_AUX,
static_cast<int32_t
>(values[0]));
624 LOGF_INFO(
"Aux moving to %.f...", values[0]);
630 RotatorAbsPosNP.
apply();
632 LOGF_INFO(
"Rotator moving to %.f ticks...", values[0]);
637 CustomRotatorStepNP.
update(values, names, n);
639 CustomRotatorStepNP.
apply();
641 auto customValue = CustomRotatorStepNP[0].getValue();
644 RotatorAbsPosNP[0].setMin(customValue / 2.0);
645 RotatorAbsPosNP[0].setMax(customValue / 2.0);
646 m_RotatorStepsPerRevolution = customValue;
647 m_RotatorTicksPerDegree = m_RotatorStepsPerRevolution / 360.0;
650 LOGF_INFO(
"Custom steps per revolution updated to %.f. Ticks per degree %.2f", customValue,
651 m_RotatorTicksPerDegree);
656 else if (strstr(name,
"ROTATOR"))
668 targetPosition = targetTicks;
684 double newPosition = 0;
716 if (isHomingComplete())
734 rc = getTemperature();
737 lastTemperature = TemperatureNP[0].getValue();
738 TemperatureNP.
apply();
745 lastVoltage = VoltageNP[0].getValue();
750 rc = getLimitSwitchStatus();
751 if (rc && (LimitSwitchLP[ROTATION_SWITCH].getState() != rotationLimit
752 || LimitSwitchLP[OUT_SWITCH].getState() != outSwitchLimit
753 || LimitSwitchLP[IN_SWITCH].getState() != inSwitchLimit))
755 rotationLimit = LimitSwitchLP[ROTATION_SWITCH].
getState();
756 outSwitchLimit = LimitSwitchLP[OUT_SWITCH].
getState();
757 inSwitchLimit = LimitSwitchLP[IN_SWITCH].
getState();
758 LimitSwitchLP.
apply();
762 bool absFocusUpdated =
false;
775 absFocusUpdated =
true;
782 absFocusUpdated =
true;
788 bool absRotatorUpdated =
false;
797 absRotatorUpdated =
true;
798 LOG_INFO(
"Rotator motion complete.");
804 while (RotatorAbsPosNP[0].getValue() < -m_RotatorStepsPerRevolution
805 || RotatorAbsPosNP[0].getValue() > m_RotatorStepsPerRevolution)
808 const auto newOffset =
static_cast<int32_t
>(RotatorAbsPosNP[0].getValue()) % m_RotatorStepsPerRevolution;
809 LOGF_INFO(
"Out of bounds value detected. Syncing rotator position to %d", newOffset);
815 lastRotatorPosition = RotatorAbsPosNP[0].getValue();
817 GotoRotatorNP[0].setValue(
range360(360 - (RotatorAbsPosNP[0].getValue() / m_RotatorTicksPerDegree)));
820 absRotatorUpdated =
true;
822 if (absRotatorUpdated)
824 RotatorAbsPosNP.
apply();
829 bool absAuxUpdated =
false;
837 absAuxUpdated =
true;
844 lastAuxPosition = GotoAuxNP[0].getValue();
845 absAuxUpdated =
true;
858bool NightCrawler::syncMotor(MotorType
type, uint32_t position)
863 int nbytes_written = 0, nbytes_read = 0, rc = -1;
866 snprintf(
cmd, 16,
"%dSP %d#",
type + 1, position);
870 tcflush(
PortFD, TCIOFLUSH);
882 LOGF_ERROR(
"%s error: %s.", __FUNCTION__, errstr);
886 res[nbytes_read] =
'\0';
890 return (res[0] ==
'#');
893bool NightCrawler::startMotor(MotorType
type)
898 int nbytes_written = 0, nbytes_read = 0, rc = -1;
901 snprintf(
cmd, 16,
"%dSM#",
type + 1);
905 tcflush(
PortFD, TCIOFLUSH);
917 LOGF_ERROR(
"%s error: %s.", __FUNCTION__, errstr);
921 res[nbytes_read] =
'\0';
925 return (res[0] ==
'#');
928bool NightCrawler::stopMotor(MotorType
type)
933 int nbytes_written = 0, nbytes_read = 0, rc = -1;
936 snprintf(
cmd, 16,
"%dSQ#",
type + 1);
940 tcflush(
PortFD, TCIOFLUSH);
952 LOGF_ERROR(
"%s error: %s.", __FUNCTION__, errstr);
956 res[nbytes_read] =
'\0';
960 return (res[0] ==
'#');
963bool NightCrawler::isMotorMoving(MotorType
type)
968 int nbytes_written = 0, nbytes_read = 0, rc = -1;
971 snprintf(
cmd, 16,
"%dGM#",
type + 1);
975 tcflush(
PortFD, TCIOFLUSH);
980 LOGF_ERROR(
"%s error: %s.", __FUNCTION__, errstr);
987 LOGF_ERROR(
"%s error: %s.", __FUNCTION__, errstr);
991 res[nbytes_read - 1] =
'\0';
995 return (strcmp(
"01", res) == 0);
998bool NightCrawler::getTemperature()
1000 char cmd[16] =
"GT#";
1003 int nbytes_written = 0, nbytes_read = 0, rc = -1;
1008 tcflush(
PortFD, TCIOFLUSH);
1013 LOGF_ERROR(
"%s error: %s.", __FUNCTION__, errstr);
1020 LOGF_ERROR(
"%s error: %s.", __FUNCTION__, errstr);
1024 res[nbytes_read - 1] =
'\0';
1028 TemperatureNP[0].setValue(atoi(res) / 10.0);
1033bool NightCrawler::getVoltage()
1035 char cmd[16] =
"GV#";
1038 int nbytes_written = 0, nbytes_read = 0, rc = -1;
1043 tcflush(
PortFD, TCIOFLUSH);
1048 LOGF_ERROR(
"%s error: %s.", __FUNCTION__, errstr);
1055 LOGF_ERROR(
"%s error: %s.", __FUNCTION__, errstr);
1059 res[nbytes_read - 1] =
'\0';
1063 VoltageNP[0].setValue(atoi(res) / 10.0);
1068bool NightCrawler::setTemperatureOffset(
double offset)
1072 int nbytes_written = 0, rc = -1;
1075 snprintf(
cmd, 16,
"Pt %03d#",
static_cast<int>(offset * 10));
1079 tcflush(
PortFD, TCIOFLUSH);
1084 LOGF_ERROR(
"%s error: %s.", __FUNCTION__, errstr);
1091bool NightCrawler::getStepDelay(MotorType
type)
1096 int nbytes_written = 0, nbytes_read = 0, rc = -1;
1099 snprintf(
cmd, 16,
"%dSR#",
type + 1);
1103 tcflush(
PortFD, TCIOFLUSH);
1115 LOGF_ERROR(
"%s error: %s.", __FUNCTION__, errstr);
1119 res[nbytes_read - 1] =
'\0';
1124 FocusStepDelayNP[0].setValue(atoi(res));
1126 RotatorStepDelayNP[0].setValue(atoi(res));
1128 AuxStepDelayNP[0].setValue(atoi(res));
1133bool NightCrawler::setStepDelay(MotorType
type, uint32_t delay)
1138 int nbytes_written = 0, nbytes_read = 0, rc = -1;
1141 snprintf(
cmd, 16,
"%dSR %03d#",
type + 1, delay);
1145 tcflush(
PortFD, TCIOFLUSH);
1150 LOGF_ERROR(
"%s error: %s.", __FUNCTION__, errstr);
1157 LOGF_ERROR(
"%s error: %s.", __FUNCTION__, errstr);
1161 res[nbytes_read] =
'\0';
1165 return (res[0] ==
'#');
1168bool NightCrawler::getLimitSwitchStatus()
1170 char cmd[16] =
"GS#";
1173 int nbytes_written = 0, nbytes_read = 0, rc = -1;
1178 tcflush(
PortFD, TCIOFLUSH);
1183 LOGF_ERROR(
"%s error: %s.", __FUNCTION__, errstr);
1190 LOGF_ERROR(
"%s error: %s.", __FUNCTION__, errstr);
1194 res[nbytes_read - 1] =
'\0';
1198 int value = atoi(res);
1207bool NightCrawler::findHome(uint8_t motorTypes)
1212 int nbytes_written = 0, nbytes_read = 0, rc = -1;
1215 snprintf(
cmd, 16,
"SH %02d#", motorTypes);
1219 tcflush(
PortFD, TCIOFLUSH);
1224 LOGF_ERROR(
"%s error: %s.", __FUNCTION__, errstr);
1231 LOGF_ERROR(
"%s error: %s.", __FUNCTION__, errstr);
1235 res[nbytes_read] =
'\0';
1239 return (res[0] ==
'#');
1242bool NightCrawler::isHomingComplete()
1245 int nbytes_read = 0, rc = -1;
1250 LOG_DEBUG(
"Waiting for NightCrawler to complete homing...");
1254 res[nbytes_read - 1] =
'\0';
1258 return (strcmp(
"OK", res) == 0);
1261bool NightCrawler::setEncodersEnabled(
bool enable)
1266 int nbytes_written = 0, nbytes_read = 0, rc = -1;
1269 snprintf(
cmd, 16,
"PE %s#", enable ?
"01" :
"00");
1273 tcflush(
PortFD, TCIOFLUSH);
1278 LOGF_ERROR(
"%s error: %s.", __FUNCTION__, errstr);
1285 LOGF_ERROR(
"%s error: %s.", __FUNCTION__, errstr);
1289 res[nbytes_read] =
'\0';
1296bool NightCrawler::setDisplayBrightness(uint8_t value)
1301 int nbytes_written = 0, nbytes_read = 0, rc = -1;
1304 snprintf(
cmd, 16,
"PD %03d#", value);
1308 tcflush(
PortFD, TCIOFLUSH);
1313 LOGF_ERROR(
"%s error: %s.", __FUNCTION__, errstr);
1320 LOGF_ERROR(
"%s error: %s.", __FUNCTION__, errstr);
1324 res[nbytes_read] =
'\0';
1328 return (res[0] ==
'#');
1331bool NightCrawler::setSleepBrightness(uint8_t value)
1336 int nbytes_written = 0, nbytes_read = 0, rc = -1;
1339 snprintf(
cmd, 16,
"PL %03d#", value);
1343 tcflush(
PortFD, TCIOFLUSH);
1348 LOGF_ERROR(
"%s error: %s.", __FUNCTION__, errstr);
1355 LOGF_ERROR(
"%s error: %s.", __FUNCTION__, errstr);
1359 res[nbytes_read] =
'\0';
1363 return (res[0] ==
'#');
1368 Focuser::saveConfigItems(fp);
1371 BrightnessNP.
save(fp);
1372 FocusStepDelayNP.
save(fp);
1373 RotatorStepDelayNP.
apply();
1374 AuxStepDelayNP.
save(fp);
1375 CustomRotatorStepNP.
save(fp);
1387 LOG_WARN(
"Homing process can take up to 10 minutes. You cannot control the unit until the process is fully complete.");
1395 LOG_ERROR(
"Failed to start homing process.");
1406 auto newAngle = ( angle > 180 ? angle - 360 : angle);
1410 auto newTarget = newAngle * m_RotatorTicksPerDegree;
1411 if (newTarget < RotatorAbsPosNP[0].getMin())
1412 newTarget = RotatorAbsPosNP[0].getMin();
1413 else if (newTarget > RotatorAbsPosNP[0].getMax())
1414 newTarget = RotatorAbsPosNP[0].getMax();
1416 bool rc = gotoMotor(
MOTOR_ROTATOR,
static_cast<int32_t
>(newTarget));
1421 RotatorAbsPosNP.
apply();
1431 auto newAngle = ( angle > 180 ? angle - 360 : angle);
1435 auto newTarget = newAngle * m_RotatorTicksPerDegree;
1436 if (newTarget < RotatorAbsPosNP[0].getMin())
1437 newTarget = RotatorAbsPosNP[0].getMin();
1438 else if (newTarget > RotatorAbsPosNP[0].getMax())
1439 newTarget = RotatorAbsPosNP[0].getMax();
1441 return syncMotor(
MOTOR_ROTATOR,
static_cast<int32_t
>(newTarget));
1450 RotatorAbsPosNP.
apply();
1460 GotoRotatorNP[0].setValue(
range360(360 - (RotatorAbsPosNP[0].getValue() / m_RotatorTicksPerDegree)));
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
virtual bool saveConfig(bool silent=false, const char *property=nullptr)
Save the current properties in a configuration file.
virtual bool Disconnect()
Disconnect from device.
void setDefaultPollingPeriod(uint32_t msec)
setDefaultPollingPeriod Change the default polling period to call TimerHit() function in the driver.
virtual void setConnected(bool status, IPState state=IPS_OK, const char *msg=nullptr)
Set connection switch status in the client.
void setDriverInterface(uint32_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
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.
uint32_t getDriverInterface() const
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
virtual bool Connect()
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
void addDebugControl()
Add Debug control to the driver.
INDI::PropertyNumber FocusRelPosNP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
INDI::PropertyNumber FocusSpeedNP
INDI::PropertyNumber FocusAbsPosNP
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,...
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Processes incoming new number values from the client for this device. Handles updates to FocuserInter...
Connection::Serial * serialConnection
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
void fill(const char *device, const char *name, const char *label, const char *group, IPState state)
bool update(const double values[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
bool update(const ISState states[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process Rotator switch properties.
bool saveConfigItems(FILE *fp)
saveConfigItems save focuser properties defined in the interface in config file
void initProperties(const char *groupName)
Initialize Rotator properties. It is recommended to call this function within initProperties() of you...
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
void SetCapability(uint32_t cap)
SetRotatorCapability sets the Rotator capabilities. All capabilities must be initialized.
INDI::PropertySwitch HomeRotatorSP
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process Rotator number properties.
INDI::PropertySwitch ReverseRotatorSP
INDI::PropertyNumber GotoRotatorNP
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
virtual IPState MoveRotator(double angle) override
MoveRotator Go to specific angle.
const char * getDefaultName() override
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 AbortRotator() override
AbortRotator Abort all motion.
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
virtual IPState HomeRotator() override
HomeRotator Go to home position.
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveFocuser the focuser to an absolute position.
virtual bool Handshake() override
Performs a device-specific handshake after a connection is established. This method should be overrid...
static void abnormalDisconnectCallback(void *userpointer)
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Processes incoming new number values from the client for this device. Handles updates to FocuserInter...
virtual bool SyncRotator(double angle) override
SyncRotator Set current angle as the supplied angle without moving the rotator.
virtual bool saveConfigItems(FILE *fp) override
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
virtual bool updateProperties() override
Updates the state and definition of properties based on the current connection status....
virtual bool ReverseRotator(bool enabled) override
ReverseRotator Reverse the direction of the rotator. CW is usually the normal direction,...
virtual bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
Provides interface to implement Rotator functionality.
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
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.
int tty_read_section(int fd, char *buf, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
double range360(double r)
range360 Limits an angle to be between 0-360 degrees.
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Implementations for common driver routines.
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.
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.
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
#define LOGF_INFO(fmt,...)
#define LOGF_DEBUG(fmt,...)
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
#define LOGF_ERROR(fmt,...)
#define NIGHTCRAWLER_TIMEOUT
#define NIGHTCRAWLER_THRESHOLD