void onCommandLoopInterval() { int loopInterval = convertToInt(gParameters[0]); if (isReadCommand(gParameters[0])) { sendInt(pidLoopInterval); sendAck(); } else if (isIntWithinRange(loopInterval, PID_INTERVAL_MS_MIN, PID_INTERVAL_MS_MAX)) { if (!isPidEnabled) { pidLoopInterval = loopInterval; sendAck(); } else { Serial.println(F("Cannot change loop interval while PID control is on.")); sendNack(); } } else { sendIntRangeError(PID_INTERVAL_MS_MIN, PID_INTERVAL_MS_MAX, MILLISECONDS_UNIT); } }
void onCommandSetPoint() { if (isChannelCorrect(gParameters[0])) { int channel = convertToInt(gParameters[0]); double setPoint = atof(gParameters[1]); if (isReadCommand(gParameters[1])) { sendDouble(setPoints[channel]); sendAck(); } else if (isDoubleWithinRange(setPoint, minSetPoint[channel], maxSetPoint[channel])) { setPoints[channel] = setPoint; sendAck(); } else { sendDoubleRangeError(minSetPoint[channel], maxSetPoint[channel], DEGREES_UNIT); } } else { sendChannelError(); } }
void onCommandRateLimit() { if (isChannelCorrect(gParameters[0])) { int channel = convertToInt(gParameters[0]); int rateLimit = convertToInt(gParameters[1]); if (isReadCommand(gParameters[1])) { sendInt(outputRateLimits[channel]); sendAck(); } else if (isIntWithinRange(rateLimit, OUTPUT_RATE_LIMIT_MIN, OUTPUT_RATE_LIMIT_MAX)) { outputRateLimits[channel] = rateLimit; sendAck(); } else { sendIntRangeError(OUTPUT_RATE_LIMIT_MIN, OUTPUT_RATE_LIMIT_MAX, DEGREES_UNIT); } } else { sendChannelError(); } }
void onCommandIntegralWindup() { if (isChannelCorrect(gParameters[0])) { int channel = convertToInt(gParameters[0]); double windup = atof(gParameters[1]); if (isReadCommand(gParameters[1])) { sendDouble(iWindupThresholds[channel]); sendAck(); } else if (isDoubleWithinRange(windup, I_WINDUP_THRESH_MIN, I_WINDUP_THRESH_MAX)) { iWindupThresholds[channel] = windup; sendAck(); } else { sendDoubleRangeError(I_WINDUP_THRESH_MIN, I_WINDUP_THRESH_MAX, DEGREES_UNIT); } } else { sendChannelError(); } }
void onCommandPidControl() { if (isReadCommand(gParameters[0])) { sendOnOffStatus(isPidEnabled); sendAck(); } else if (isOnCommandArg(gParameters[0])) { if (!isPidEnabled) { enablePid(); sendAck(); } else { Serial.println(F("PID control is already on.")); sendNack(); } } else if (isOffCommandArg(gParameters[0])) { disablePid(); sendAck(); } else { sendOnOffError(); } }
// Allows the reading or changing of direction for specified motor, but only allows change in // direction if the change results in a percentage change of less than the outputRateLimit void onCommandDirection() { if (isChannelCorrect(gParameters[0])) { uint8_t channel = convertToInt(gParameters[0]); if (isReadCommand(gParameters[1])) { sendDirectionStatus(directions[channel]); sendAck(); } else if (!isPidEnabled) { int output = currentOutputs[channel]; int outputRateLimit = outputRateLimits[channel]; Direction direction; if (isClockwiseCommandArg(gParameters[1])) { direction = Direction::Clockwise; } else if (isCounterClockwiseCommandArg(gParameters[1])) { direction = Direction::CounterClockwise; } else { sendDirectionError(); return; } if (direction != directions[channel] && output > outputRateLimit / 2) { Serial << F("Cannot make a change to output by more than ") << outputRateLimit << F("%") << NEWLINE; sendNack(); } else { applyMotorOutputs(channel, direction, output); sendAck(); } } else { Serial.println(F("Cannot change direction while PID control is on.")); sendNack(); } } else { sendChannelError(); } }
void onCommandMotorDriver() { if (isChannelCorrect(gParameters[0])) { uint8_t channel = convertToInt(gParameters[0]); if (isReadCommand(gParameters[1])) { sendMotorDriverStatus(motorDriverTypes[channel]); sendAck(); } else { if (!isPidEnabled) { if (isAnalogVoltageCommandArg(gParameters[1])) { motorDriverTypes[channel] = AnalogVoltage; sendAck(); } else if (isFrequencyCommandArg(gParameters[1])) { if (channel == TILT_CHANNEL) { motorDriverTypes[channel] = Frequency; sendAck(); } else { Serial.println(F("Yaw (Channel 1) can only be set to Analog Voltage")); sendNack(); } } else { sendMotorDriverError(); } } else { Serial.println(F("Cannot change driver type while PID control is on.")); sendNack(); } } } else { sendChannelError(); } }
void Receiver::handleMsg(Msg* msg) { // ACK package int sequenceNumber = msg->sequenceNumber; int windowEnd = (windowOrigin + sequenceSeed) % SEQUENCE_RANGE + WINDOW_SIZE; if (msg->sequenceNumber == (sequenceSeed + 1) % SEQUENCE_RANGE && msg->type == RESP_ERR && strcmp(msg->buffer, "NSF") == 0) { sendAck(sequenceNumber); throw std::exception("No such file."); } // Handle EOF if (isEofMsg(msg)) { int expectedSize; memcpy(&expectedSize, msg->buffer + 4, sizeof(expectedSize)); if (expectedSize == totalPayloadSize) { windowOrigin++; isComplete = true; sendAck(sequenceNumber); } return; } sendAck(sequenceNumber); if (!isSequenceNumberInWindow(sequenceNumber)) return; // Register package in buffer if (currentWindow[sequenceNumber] != NULL) return; int currentPayloadLength = msg->length; char* currentData = new char[currentPayloadLength]; memcpy(currentData, msg->buffer, currentPayloadLength); Payload* currentPayload = new Payload(); currentPayload->length = currentPayloadLength; currentPayload->data = currentData; currentWindow[sequenceNumber] = currentPayload; totalPayloadSize += currentPayloadLength; // It doesn't matter if it's out of order. // Normalize buffer int currentSequence = (windowOrigin + sequenceSeed) % SEQUENCE_RANGE; while (currentWindow[currentSequence] != NULL) { completedSet.push(currentWindow[currentSequence]); currentWindow[currentSequence] = NULL; currentSequence = (++windowOrigin + sequenceSeed) % SEQUENCE_RANGE; } }
/** "SC" — Stepper and Servo Mode Configure Command: SC,value1,value2<CR> Response: OK<NL><CR> Firmware versions: All Execution: Immediate Arguments: value1 is an integer in the range from 0 to 255, which specifies the parameter that you are adjusting. value2 is an integer in the range from 0 to 65535. It specifies the value of the parameter given by value1. See the list of these parameters (value1) and allowed values (value2), below. Description:00 This command allows you to configure the motor control modes that the EBB uses, including parameters of the servo or solenoid motor used for raising and lowering the pen, and how the stepper motor driver signals are directed. The set of parameters and their allowed values is as follows: SC,1,value2 Pen lift mechanism. value2 may be 0, 1 or 2. Early EggBot models used a small solenoid, driven from an output signal on pin RB4. SC,1,0 Enable only the solenoid output (RB4) for pen up/down movement. SC,1,1 Enable only the RC servo output (RB1) for pen up/down movement. SC,1,2 Enable both the solenoid (RB4) and RC servo (RB1) outputs for pen up/down movement (default) SC,2,value2 Stepper signal control. value2 may be 0, 1 or 2. SC,2,0 Use microcontroller to control on-board stepper driver chips (default) SC,2,1 Disconnect microcontroller from the on-board stepper motor drivers and drive external step/direction motor drivers instead. In this mode, you can use the microcontroller to control external step/direction drivers based on the following pin assignments: ENABLE1: RD1 ENABLE2: RA1 STEP1: RC6 DIR1: RC2 STEP2: RA5 DIR2: RA2 Note also that in this mode, you can externally drive the step/direction/enable lines of the on board stepper motor drivers from the pins of J4 and J5. (Please refer to the schematic for where these pins are broken out.) SC,2,2 Disconnect microcontroller from both the built-in motor drivers and external pins. All step/dir/enable pins on the PIC are set to inputs. This allows you to control the on-board stepper motor driver chips externally with your own step/dir/enable signals. Use the pins listed in the schematic from J5 and J4. SC,4,servo_min Set the minimum value for the RC servo output position. servo_min may be in the range 1 to 65535, in units of 83 ns intervals. This sets the "Pen Up" position. Default: 12000 (1.0 ms) on reset. SC,5,servo_max Set the maximum value for the RC servo output position. servo_max may be in the range 1 to 65535, in units of 83 ns intervals. This sets the "Pen Down" position. Default: 16000 (1.33 ms) on reset. SC,8,maximum_S2_channels Sets the number of RC servo PWM channels, each of S2_channel_duration_ms before cycling back to channel 1 for S2 command. Values from 1 to 24 are valid for maximum_S2_channels. Default: 8 on reset. SC,9,S2_channel_duration_ms Set the number of milliseconds before firing the next enabled channel for the S2 command. Values from 1 to 6 are valid for S2_channel_duration_ms. Default: 3 ms on reset. SC,10,servo_rate Set rate of change of the servo position, for both raising and lowering movements. Same units as rate parameter in S2 command. SC,11,servo_rate_up Set the rate of change of the servo when going up. Same units as rate parameter in S2 command. SC,12,servo_rate_down Set the rate of change of the servo when going down. Same units as rate parameter in S2 command. SC,13,use_alt_pause - turns on (1) or off (0) alternate pause button function on RB0. On by default. For EBB v1.1 boards, it uses RB2 instead. Example: SC,4,8000\r Set the pen-up position to give a servo output of 8000, about 0.66 ms. Example: SC,1,1\r Enable only the RC servo for pen lift; disable solenoid control output. */ void EBBParser::parseSC(const char* arg1, const char* arg2) { if (arg1 == NULL || arg2 == NULL) { sendError(); return; } int cmd = atoi(arg1); int value = atoi(arg2); switch (cmd) { case 4: setPenUpPos(value / 240 - 25); break; case 5: setPenDownPos(value / 240 - 25); break; case 6: // rotMin=value; ignored break; case 7: // rotMax=value; ignored break; case 11: setServoRateUp(value / 5); break; case 12: setServoRateDown(value / 5); break; default: sendError(); return; } sendAck(); }
void playSolitaire(void *client) { // initialize deal_t *deal = newDeal(deck); play_t play; int status = SUCCESS; while (1) { putArena(arena); printf("\n"); putDeal(deal); printf("\nEnter your play: "); getNextPlay(client,&play,deck); switch (play.type) { case ARENA_PLAY: // Play a card into the arena. status = makeArenaPlay(play.from,arena); break; case KING_PLAY: // Move a king card onto an empty lain stack. status = moveKingOntoFree(play.from,deal); break; case MOVE_PLAY: // Move a card (possibly with cards above it) onto some lain stack. status = moveCardOntoAnother(play.from,play.onto); break; case DRAW_PLAY: // Draws the next card and puts it on top of the discard pile. status = pullFromDrawPile(deal); break; } sendAck(client,status); } }
/** * Recieves a datapacket, sends it to the correct app * @param recvd Recieved packet * @param datalen Length of recieved data * @param srcmip Source MIP of packet */ void recvData(struct tp_packet *recvd, uint16_t datalen, uint8_t srcmip) { //if(debug) fprintf(stderr, "MIPTP: recvData(%p, %d, %d)\n", recvd, datalen, srcmip); struct applist *app = NULL; getApp(recvd->port, &app, approot); if(debug || seqnodb) fprintf(stderr, "MIPTP: Recieved data with len %d and SN %d from MIP %d to port %d.", datalen, recvd->seqno, srcmip, recvd->port); if(debug || seqnodb) { if(app == NULL) fprintf(stderr, " No app on that port\n"); else fprintf(stderr, "\n"); } if(app == NULL) return; if(recvd->seqno == app->recvinfo->nextRecvSeqno) { // Accept packet app->recvinfo->nextRecvSeqno++; addPacket(recvd, srcmip, datalen, app->recvinfo->recvQueue); while(containsSeqno(app->recvinfo->nextRecvSeqno, app->recvinfo->recvQueue)) app->recvinfo->nextRecvSeqno++; if(debug) fprintf(stderr, "MIPTP: Packet accepted, now waiting for SN %d\n", app->recvinfo->nextRecvSeqno); } else if(recvd->seqno > app->recvinfo->nextRecvSeqno) { // Queue packet while waiting for a lost one addPacket(recvd, srcmip, datalen, app->recvinfo->recvQueue); if(debug) fprintf(stderr, "MIPTP: Packet buffered, still waiting for SN %d\n", app->recvinfo->nextRecvSeqno); } if(app->lastTimeout != 0) app->lastTimeout = 0; //fprintf(stderr, "MIPTP: R-DAT %d FROM %d\n", recvd->seqno, srcmip); sendAck(app, recvd->port, srcmip); }
bufferStruct receiveSendData(bufferStruct buffer){ bufferStruct result; if(getAck(buffer.bufferLength, buffer.buffer) & NEED_ACK > 0){ #if STATE_UART_DEBUG >= 2 uart_putstr ("seAck\r\n"); #endif state = STATE_SENDING_ACK; backupData.bufferLength = buffer.bufferLength; memcpy(backupData.buffer, buffer.buffer, buffer.bufferLength); sendAck(buffer); } else { result.bufferLength = getPayloadLength(buffer.bufferLength, buffer.buffer); memcpy(result.buffer, getPayload(buffer.bufferLength, buffer.buffer), result.bufferLength); #if STATE_UART_DEBUG >= 3 uart_putc('%'); uart_putc(result.bufferLength); uart_putc('%'); for(uint8_t i = 0; i < result.bufferLength; ++i){ uart_putc(result.buffer[i]); } uart_putc('%'); #endif } return result; }
// This command is complex because it only allows setting of frequency for channel 1 // This is because only the tilt motor can be controlled by frequency void onCommandFrequencyOutput() { if (isChannelCorrect(gParameters[0])) { uint8_t channel = convertToInt(gParameters[0]); uint16_t frequency = convertToUint(gParameters[1]); if (channel == TILT_CHANNEL) { if (isReadCommand(gParameters[1])) { sendInt(currentFrequency); sendAck(); } else { if (!isSafetyOn) { if (isIntWithinRange(frequency, MOTOR_MIN_FREQUENCY, MOTOR_MAX_FREQUENCY)) { setFrequency(frequency); sendAck(); } else { sendIntRangeError(MOTOR_MIN_FREQUENCY, MOTOR_MAX_FREQUENCY, HERTZ_UNIT); } } else { Serial.println(F("Cannot change frequency output while safety is on.")); sendNack(); } } } else { Serial.println(F("Changing or reading of frequency only applies to channel 1")); sendNack(); } } else { sendChannelError(); } }
/** "TP" — Toggle Pen Command: TP[,duration]<CR> Response: OK<NL><CR> Firmware versions: v1.9 and newer Execution: Immediate Arguments: duration: (Optional) an integer in the range of 1 to 65535, giving an delay in milliseconds. Description: This command toggles the state of the pen (up->down and down->up). EBB firmware resets with pen in 'up' (servo_min) state. Note that conventionally, we have used the servo_min ("SC,4") value as the 'Pen up position', and the servo_max ("SC,5") value as the 'Pen down position'. The optional duration argument is in milliseconds. It represents the total length of time between when the pen move is started, and when the next command will be executed. Note that this is not related to how fast the pen moves, which is set with the SC command. Rather, it is an intentional delay of a given duration, to force the EBB not to execute the next command (often an SM) for some length of time, which allows the pen move to complete and possibly some extra settling time before moving the other motors. If no duration argument is specified, a value of 0 milliseconds is used internally. */ void EBBParser::parseTP(const char* arg) { sendAck(); const short delayMs = (arg != NULL) ? atoi(arg) : 0; setPenState(!getPenState(), delayMs); }
void EBBParser::parseStream() { if (!mStream.available()) return; const char inChar = mStream.read(); if (inChar != '\r') { if (mReadBuffer.length() < 64 && isprint(inChar)) mReadBuffer += inChar; return; } char* str = mReadBuffer.begin(); const char* cmd = strsep(&str, ","); const char* arg1 = strsep(&str, ","); const char* arg2 = strsep(&str, ","); const char* arg3 = strsep(&str, ","); if (strcmp(cmd, "EM") == 0) { parseEM(arg1, arg2); } else if (strcmp(cmd, "ND") == 0) { parseND(); } else if (strcmp(cmd, "NI") == 0) { parseNI(); } else if (strcmp(cmd, "PD") == 0) { sendAck(); } else if (strcmp(cmd, "PO") == 0) { parsePO(arg1, arg2, arg3); } else if (strcmp(cmd, "QB") == 0) { parseQB(); } else if (strcmp(cmd, "QL") == 0) { parseQL(); } else if (strcmp(cmd, "QN") == 0) { parseQN(); } else if (strcmp(cmd, "QP") == 0) { parseQP(); } else if (strcmp(cmd, "SC") == 0) { parseSC(arg1, arg2); } else if (strcmp(cmd, "SE") == 0) { parseSE(arg1, arg2, arg3); } else if (strcmp(cmd, "SL") == 0) { parseSL(arg1); } else if (strcmp(cmd, "SM") == 0) { parseSM(arg1, arg2, arg3); } else if (strcmp(cmd, "SN") == 0) { parseSN(arg1); } else if (strcmp(cmd, "SP") == 0) { parseSP(arg1, arg2, arg3); } else if (strcmp(cmd, "TP") == 0) { parseTP(arg1); } else if (strcmp(cmd, "v") == 0) { parseV(); } else sendError(); mReadBuffer = ""; }
/* timeoutHandler - This function is called on the 500msec timeout and individual/cumulative ack is sent to the server depending on the number of packets that have been received */ void timeoutHandler(void) { sendAck(ackSeqNumber, receiveWindowFree); it_val.it_interval.tv_sec = 0; it_val.it_interval.tv_usec = 0; it_val.it_value.tv_sec = 0; it_val.it_value.tv_usec = 0; setitimer(ITIMER_REAL, &it_val, 0); }
/// <summary> /// Receive the specified buffer. /// </summary> /// <param name='buffer'> /// Buffer. /// </param> short Transport::receive(char buf[], short size) { short bytesRead; bytesRead = link->receive(this->buffer,size+ACKSIZE); while(!checksum->checkChecksum(this->buffer,bytesRead)) { sendAck(false); bytesRead = link->receive(this->buffer,size+ACKSIZE); std::cout<<"Error in checksum\r\n"; } sendAck(true); std::cout<<"Checksum Ok\r\n"; memcpy(buf,buffer+ACKSIZE,bytesRead-ACKSIZE); return bytesRead-ACKSIZE; }
/** "SL" — Set Layer Command: SL,NewLayerValue<CR> Response: OK<NL><CR> Firmware versions: v1.9.2 and newer Execution: Immediate Arguments: NewLayerValue is an integer between 0 and 127. Description: This command sets the value of the Layer variable, which can be read by the QL query. This variable is a single unsigned byte, and is available for the user to store a single variable as needed. Example: SL,4\r Set the Layer variable to 4. Example: SL,125\r Set the Layer variable to 125. Version History: Added in v1.9.2 */ void EBBParser::parseSL(const char* arg) { if (arg == NULL) { sendError(); return; } sendAck(); mLayer = atoi(arg); }
/** "SN" — Set node count Command: SN,value<CR> Response: OK<NL><CR> Firmware versions: v1.9.5 and newer Execution: Immediate Arguments: value is an unsigned long (four byte) integer. Description: This command sets the Node Counter to value. See the "QN" command for a description of the node counter and its operations. Example: SN,123456789\r Set node counter to 123456789. Version History: Added in v1.9.5 */ void EBBParser::parseSN(const char* arg) { if (arg == NULL) { sendError(); return; } sendAck(); mNodeCount = atoi(arg); }
/** "PO" — Pin Output Command: PO,Port,Pin,Value<CR> Response: OK<NL><CR> Firmware versions: All Execution: Immediate Arguments: Port: is one of the following letters: A,B,C,D,E. It specifies which port on the processor is to be used for the output. Pin: is an integer in the range from 0 through 7. It specifies the pin to be used for the output. Value: is either 0 or 1. It specifies the logical value to be output on the pin. Description: This command outputs a digital value of a 0 (0V) or 1 (3.3V) on one of the pins on the processor, as specified by Port and Pin. This command will not change a pin's direction to output first, so you must set the pin's direction to be an output using the PD command first if you want anything to come out of the pin. This command is a very low-level I/O command. Many other higher level commands (like SM, S2, etc.) will over-write the output state of pins that they need. This commands allows you low-level access to every pin on the processor. Example: PO,C,7,1\r This command would set the pin RC7 (or Port C, pin 7) to a high value. */ void EBBParser::parsePO(const char* arg1, const char* arg2, const char* arg3) { if (arg1 == NULL || arg2 == NULL || arg3 == NULL) { sendError(); return; } sendAck(); setPinOutput(arg1[0], atoi(arg2), atoi(arg3)); }
void onCommandZeroEncoderAngle() { if (isReadCommand(gParameters[1])) { zeroEncoderAngle(); sendAck(); } else { sendReadOnlyError(); } }
void onCommandEcho() { if (gParameters[0] != NULL) { Serial.println(gParameters[0]); sendAck(); } else { sendNack(); } }
void onCommandVersion() { if (isReadCommand(gParameters[0])) { sendDouble(FIRMWARE_VERSION); sendAck(); } else { sendReadOnlyError(); } }
void onCommandChangelog() { if (isReadCommand(gParameters[0])) { Serial.println(F("Added PING command")); sendAck(); } else { sendReadOnlyError(); } }
void ServiceServerThread::parseData(const QString &dataReceived) { QStringList splitData = dataReceived.split(" "); if(splitData.size() > 1) { QString numberOfChars = splitData.at(0); bool ok; int messageLength = numberOfChars.toInt(&ok); if(ok) { QString message = dataReceived.mid(numberOfChars.size()+1,messageLength); if( message == MSG_UIFORCAR ) { sendAck(); emit forcarColeta(); } else if( message == MSG_UIFINALIZAR ) { sendAck(); emit finalizarCacic(); } } } }
void onCommandSafety() { if (isReadCommand(gParameters[0])) { sendOnOffStatus(isSafetyOn); sendAck(); } else if (isOnCommandArg(gParameters[0])) { isSafetyOn = true; sendAck(); } else if (isOffCommandArg(gParameters[0])) { isSafetyOn = false; sendAck(); } else { sendOnOffError(); } }
void onCommandVerbose() { if (isReadCommand(gParameters[0])) { sendOnOffStatus(isVerboseMode); sendAck(); } else if (isOnCommandArg(gParameters[0])) { isVerboseMode = true; sendAck(); } else if (isOffCommandArg(gParameters[0])) { isVerboseMode = false; sendAck(); } else { sendOnOffError(); } }
/** "SE" — Set Engraver Command: SE,state[,power[,use_motion_queue]]<CR> Response: OK<NL><CR> Firmware versions: v2.1.0 and newer (with changes) Execution: Added to FIFO motion queue Arguments: state may be either 0 to disable or 1 to enable the engraver output. power is an optional argument, with allowed values of integers in the range 0 to 1023. use_motion_queue is an optional argument, with allowed values of 0 (immediate) or 1 (use motion queue). Description: This command is used to enable and disable the engraver PWM output on RB3 (called B3 on the board), and also set its output power. Use SE,0 to disable this feature. The power argument represents the power (duty cycle of the PWM signal), where 0 is always off and 1023 is always on. If this optional argument is not included, then the power will be set at 512 (50%) duty cycle. If the use_motion_queue parameter has the value of 1, then this SE command will be added to the motion queue just like SM and SP commands, and thus will be executed when the previous motion commands have finished. Note that if you need to use this argument, the power argument is not optional. If use_motion_queue has value 0 (or if it is omitted) the command is executed immediately, and is not added to the queue. Example: SE,1,1023\r Turns on the engraver output with maximum power Example: SE,0\r Turns off the engraver output Example: SE,0,0,1\r Adds a command to the motion queue, that (when executed) turns off the engraver output. */ void EBBParser::parseSE(const char* arg1, const char* arg2, const char* arg3) { if (arg1 == NULL) { sendError(); return; } sendAck(); const bool state = atoi(arg1); const int power = (arg2 != NULL) ? atoi(arg2) : 512; setEngraverState(state, power); }
void CDStarControl::clock(unsigned int ms) { if (m_network != NULL) writeNetwork(); m_ackTimer.clock(ms); if (m_ackTimer.isRunning() && m_ackTimer.hasExpired()) { sendAck(); m_ackTimer.stop(); } m_holdoffTimer.clock(ms); if (m_holdoffTimer.isRunning() && m_holdoffTimer.hasExpired()) m_holdoffTimer.stop(); m_timeoutTimer.clock(ms); if (m_state == RS_RELAYING_NETWORK_AUDIO) { m_networkWatchdog.clock(ms); if (m_networkWatchdog.hasExpired()) { // We're received the header haven't we? m_frames += 1U; if (m_bits == 0U) m_bits = 1U; LogMessage("D-Star, network watchdog has expired, %.1f seconds, %u%% packet loss, BER: %.1f%%", float(m_frames) / 50.0F, (m_lost * 100U) / m_frames, float(m_errs * 100U) / float(m_bits)); m_timeoutTimer.stop(); writeEndOfTransmission(); #if defined(DUMP_DSTAR) closeFile(); #endif } } if (m_state == RS_RELAYING_NETWORK_AUDIO) { m_packetTimer.clock(ms); if (m_packetTimer.isRunning() && m_packetTimer.hasExpired()) { unsigned int frames = m_elapsed.elapsed() / DSTAR_FRAME_TIME; if (frames > m_frames) { unsigned int count = frames - m_frames; if (count > 3U) { LogMessage("D-Star, lost audio for 300ms filling in, %u %u", frames, m_frames); insertSilence(count - 1U); } } m_packetTimer.start(); } } }
void onCommandDacVoltage() { if (isChannelCorrect(gParameters[0])) { uint8_t channel = convertToInt(gParameters[0]); double voltage = atof(gParameters[1]); if (isReadCommand(gParameters[1])) { sendDouble(currentVoltages[channel]); sendAck(); } else { if (!isSafetyOn) { if (isDoubleWithinRange(voltage, MOTOR_MIN_VOLTAGE, MOTOR_MAX_VOLTAGE)) { setDacVoltage(channel, voltage); sendAck(); } else { sendDoubleRangeError(MOTOR_MIN_VOLTAGE, MOTOR_MAX_VOLTAGE, VOLTAGE_UNIT); } } else { Serial.println(F("Cannot change voltage while safety is on.")); sendNack(); } } } else { sendChannelError(); } }