// in an previous version white space was allowed befor the command, but not now. // a command string always starts at postion 2 and ends at the first white space // the command looks like an MQTT topic or the directory structure of a file system // e.g. /0/pwm 127 // find end of command and place a null termination so it can be used as a string uint8_t findCommand(void) { uint8_t lastAlpha =2; // the command always starts after the addrss at position 2 command = command_buf + lastAlpha; // Only an isspace or null may terminate a valid command. // The command is made of chars of isalpa, '/', or '?'. while( !( isspace(command_buf[lastAlpha]) || (command_buf[lastAlpha] == '\0') ) && lastAlpha < (COMMAND_BUFFER_SIZE-1) ) { if ( isalpha(command_buf[lastAlpha]) || (command_buf[lastAlpha] == '/') || (command_buf[lastAlpha] == '?') ) { lastAlpha++; } else { if (echo_on) printf_P(PSTR("{\"err\": \"BadCharInCmd '%c'\"}\r\n"),command_buf[lastAlpha]); initCommandBuffer(); return 0; } } // command does not fit in buffer if ( lastAlpha >= (COMMAND_BUFFER_SIZE-1) ) { if (echo_on) printf_P(PSTR("{\"err\": \"HugeCmd\"}\r\n")); initCommandBuffer(); return 0; } if ( isspace(command_buf[lastAlpha]) ) { // the next poistion may be an argument. if ( findArgument(lastAlpha+1) ) { // replace the space with a null so command works as a null terminated string. command_buf[lastAlpha] = '\0'; } else { // isspace() found after command but argument was not valid if (echo_on) printf_P(PSTR("{\"err\": \"CharAftrCmdBad '%c'\"}\r\n"),command_buf[lastAlpha+1]); initCommandBuffer(); return 0; } } else { if (command_buf[lastAlpha] != '\0') { // null must end command. if (echo_on) printf_P(PSTR("{\"err\": \"MissNullAftrCmd '%c'\"}\r\n"),command_buf[lastAlpha]); initCommandBuffer(); return 0; } } // zero indexing is also the count and should match with strlen() return lastAlpha; }
// pinMode( arg[0], arg[1] ) void Mode(void) { if ( (command_done == 10) ) { // check that arg[0] is a digit if ( ( !( isdigit(arg[0][0]) ) ) ) { printf_P(PSTR("{\"err\":\"pModeNaN\"}\r\n")); initCommandBuffer(); return; } // and arg[0] value is 2|3|24|25|26|27 uint8_t a = atoi(arg[0]); if ( (a != 2) && (a != 3) && ( ( a < 24) || (a > 27) ) ) { printf_P(PSTR("{\"err\":\"pModeOutOfRng\"}\r\n")); initCommandBuffer(); return; } // also arg[1] is not ('INPUT' or 'OUTPUT') if ( !( (strcmp_P( arg[1], PSTR("INPUT")) == 0) || (strcmp_P( arg[1], PSTR("OUTPUT")) == 0) ) ) { printf_P(PSTR("{\"err\":\"pModeNaMode\"}\r\n")); initCommandBuffer(); return; } serial_print_started_at = millis(); if (strcmp_P( arg[1], PSTR("OUTPUT")) == 0 ) { pinMode(a, OUTPUT); } else { pinMode(a, INPUT); } printf_P(PSTR("{\"")); command_done = 11; } else if ( (command_done == 11) ) { echo_digital_pin_in_json_rply(); printf_P(PSTR("\":\"")); command_done = 12; } else if ( (command_done == 12) ) { printf( arg[1] ); printf_P(PSTR("\"}\r\n")); initCommandBuffer(); } else { printf_P(PSTR("{\"err\":\"pModeCmdDnWTF\"}\r\n")); initCommandBuffer(); } }
// digitalToggle( arg[0] ) void Toggle(void) { if ( (command_done == 10) ) { // check that arg[0] is a digit if ( ( !( isdigit(arg[0][0]) ) ) ) { printf_P(PSTR("{\"err\":\"dTogNaN\"}\r\n")); initCommandBuffer(); return; } // and arg[0] value is 2|3|24|25|26|27 uint8_t a = atoi(arg[0]); if ( (a != 2) && (a != 3) && ( ( a < 24) || (a > 27) ) ) { printf_P(PSTR("{\"err\":\"dTogOutOfRng\"}\r\n")); initCommandBuffer(); return; } serial_print_started_at = millis(); digitalToggle(a); printf_P(PSTR("{\"")); command_done = 11; } else if ( (command_done == 11) ) { echo_digital_pin_in_json_rply(); printf_P(PSTR("\":\"")); command_done = 12; } else if ( (command_done == 12) ) { uint8_t a = atoi(arg[0]); if ( (a > (NUM_DIGITAL_PINS-1)) ) // the badPinCheck will barf at compile time without testing the value ... amazing { return; } bool pin = digitalRead(a); if (pin) { printf_P(PSTR("HIGH")); } else { printf_P(PSTR("LOW")); } printf_P(PSTR("\"}\r\n")); initCommandBuffer(); } else { printf_P(PSTR("{\"err\":\"dTogCmdDnWTF\"}\r\n")); initCommandBuffer(); } }
void PM3Monitor::reset() { initCommandBuffer(); // Reset // addCSafeData(CSAFE_GOFINISHED_CMD); addCSafeData(CSAFE_GOIDLE_CMD); executeCSafeCommand("go idle tkcmdsetCSAFE_command" ); initCommandBuffer(); // Start. addCSafeData(CSAFE_GOHAVEID_CMD); addCSafeData(CSAFE_GOINUSE_CMD); executeCSafeCommand("go in use tkcmdsetCSAFE_command" ); try { } catch (...) { throw; //some how this somtimes goes wrong, ignore because it still works fine //todo find out why and fix it } initCommandBuffer(); addCSafeData(CSAFE_RESET_CMD); executeCSafeCommand("reset tkcmdsetCSAFE_command" ); }
// assemble command line from incoming char's void AssembleCommand(char input) { // a return or new-line finishes the line (or starts a new command line) if ( (input == '\r') || (input == '\n') ) // pressing enter in picocom sends a \r { //echo both carrage return and newline. if (echo_on) printf("\r\n"); // finish command line as a null terminated string command_buf[command_head] = '\0'; // do not go past the buffer if (command_head < (COMMAND_BUFFER_SIZE - 1) ) { ++command_head; } else // command is to big { if (echo_on) printf_P(PSTR("Ignore_Input\r\n")); initCommandBuffer(); } command_done = 1; } else { //echo the input if (echo_on) printf("%c", input); // assemble the command command_buf[command_head] = input; // do not go past the buffer if (command_head < (COMMAND_BUFFER_SIZE - 1) ) { ++command_head; } else // command is to big { command_buf[1] = '\0'; if (echo_on) printf_P(PSTR("Ignore_Input\r\n")); echo_on = 0; } } }
void PM3Monitor::accumulateForceCurve() { initCommandBuffer(); addCSafeData(CSAFE_SETUSERCFG1_CMD); addCSafeData(0x03); addCSafeData(CSAFE_PM_GET_FORCEPLOTDATA); addCSafeData(0x01); addCSafeData(0x20); // Handle power curve. uint nPointsReturned = 0xFF; while (0 < nPointsReturned) { // Get any points available and consume them into the array. _rsp_data_size = CM_DATA_BUFFER_SIZE; //clear just in case this may be the problem for (int i=0;i<CM_DATA_BUFFER_SIZE;i++) _rsp_data[i] =0; executeCSafeCommand("accumulateForceCurve tkcmdsetCSAFE_command"); nPointsReturned = _rsp_data[4]; short unsigned int orgCount = _strokeData.forcePlotCount; for (unsigned short int i = 0; i < nPointsReturned; i += 2) { _strokeData.forcePlotPoints[_strokeData.forcePlotCount] = _rsp_data[5 + i] + (_rsp_data[6 + i] << 8); (_strokeData.forcePlotCount)++; if (_strokeData.forcePlotCount>=MAX_PLOT_POINTS) throw PM3Exception(ERROR_POINT_BUFFER_OVERFLOW,"Plot point buffer overflow"); } if (nPointsReturned>0) incrementalPowerCurveUpdate(_strokeData.forcePlotPoints, orgCount, _strokeData.forcePlotCount); } }
void setup(void) { pinMode(STATUS_LED,OUTPUT); digitalWrite(STATUS_LED,HIGH); // Initialize Timers, ADC, and clear bootloader, Arduino does these with init() in wiring.c initTimers(); //Timer0 Fast PWM mode, Timer1 & Timer2 Phase Correct PWM mode. init_ADC_single_conversion(EXTERNAL_AVCC); // warning AREF must not be connected to anything init_uart0_after_bootloader(); // bootloader may have the UART setup // put ADC in Auto Trigger mode and fetch an array of channels enable_ADC_auto_conversion(BURST_MODE); adc_started_at = millis(); /* Initialize UART, it returns a pointer to FILE so redirect of stdin and stdout works*/ stdout = stdin = uartstream0_init(BAUD); /* Initialize I2C, with the internal pull-up note: I2C scan will stop without a pull-up on the bus */ twi_init(TWI_PULLUP); /* Clear and setup the command buffer, (probably not needed at this point) */ initCommandBuffer(); // Enable global interrupts to start TIMER0 and UART ISR's sei(); blink_started_at = millis(); rpu_addr = get_Rpu_address(); blink_delay = BLINK_DELAY; // blink fast if a default address from RPU manager not found if (rpu_addr == 0) { rpu_addr = '0'; blink_delay = BLINK_DELAY/4; } }
void PM3Monitor::lowResolutionUpdate() { int oldforcePlotCount = _strokeData.forcePlotCount-1; //search for the part where the curve ended (this is where power is low and power is going down unsigned int prefStrokeData=10000; for( int i= _strokeData.forcePlotCount-1;i>=(oldforcePlotCount /2);i--) //search from end till the middle { if ( (prefStrokeData<15) && //it is low , must be at end of the stroke (_strokeData.forcePlotPoints[i] > prefStrokeData )//it is rising again ) { _strokeData.forcePlotCount=i+2; //use the previous one which was less break; //found the real end of the stroke } prefStrokeData=_strokeData.forcePlotPoints[i]; } initCommandBuffer(); // Header and number of extension commands. addCSafeData(CSAFE_SETUSERCFG1_CMD); addCSafeData( 0x03); // Three PM3 extension commands. addCSafeData(CSAFE_PM_GET_DRAGFACTOR); addCSafeData(CSAFE_PM_GET_WORKDISTANCE); addCSafeData(CSAFE_PM_GET_WORKTIME); // Standard commands. addCSafeData(CSAFE_GETPACE_CMD); addCSafeData(CSAFE_GETPOWER_CMD); addCSafeData(CSAFE_GETCADENCE_CMD); executeCSafeCommand("lowResolutionUpdate tkcmdsetCSAFE_command" ); uint currentbyte = 0; uint datalength = 0; if (_rsp_data[currentbyte] == CSAFE_SETUSERCFG1_CMD) { currentbyte += 2; } if (_rsp_data[currentbyte] == CSAFE_PM_GET_DRAGFACTOR) { currentbyte++; datalength = _rsp_data[currentbyte]; currentbyte++; _strokeData.dragFactor = _rsp_data[currentbyte]; currentbyte += datalength; } if (_rsp_data[currentbyte] == CSAFE_PM_GET_WORKDISTANCE) { currentbyte++; datalength = _rsp_data[currentbyte]; currentbyte++; uint distanceTemp = (_rsp_data[currentbyte] + (_rsp_data[currentbyte + 1] << 8) + (_rsp_data[currentbyte + 2] << 16) + (_rsp_data[currentbyte + 3] << 24)) / 10; //uint fractionTemp = _rsp_data[currentbyte + 4]; _strokeData.workDistance = distanceTemp; currentbyte += datalength; } if (_rsp_data[currentbyte] == CSAFE_PM_GET_WORKTIME) { currentbyte++; datalength = _rsp_data[currentbyte]; currentbyte++; if (datalength == 5) { uint timeInSeconds = (_rsp_data[currentbyte] + (_rsp_data[currentbyte + 1] << 8) + (_rsp_data[currentbyte + 2] << 16) + (_rsp_data[currentbyte + 3] << 24)) / 100; uint fraction = _rsp_data[currentbyte + 4]; _strokeData.workTime = timeInSeconds + (fraction / 100.0); _strokeData.workTimehours = timeInSeconds / 3600; _strokeData.workTimeminutes = (timeInSeconds / 60) % 60; _strokeData.workTimeseconds = timeInSeconds % 60; } currentbyte += datalength; } if (_rsp_data[currentbyte] == CSAFE_GETPACE_CMD) { currentbyte++; datalength = _rsp_data[currentbyte]; currentbyte++; // Pace is in seconds/Km uint pace = _rsp_data[currentbyte] + (_rsp_data[currentbyte + 1] << 8); // get pace in seconds / 500m double fPace = pace / 2.0; // convert it to mins/500m _strokeData.splitMinutes = floor(fPace / 60); _strokeData.splitSeconds = fPace - (_strokeData.splitMinutes * 60); currentbyte += datalength; } if (_rsp_data[currentbyte] == CSAFE_GETPOWER_CMD) { currentbyte++; datalength = _rsp_data[currentbyte]; currentbyte++; _strokeData.power = _rsp_data[currentbyte] + (_rsp_data[currentbyte + 1] << 8); currentbyte += datalength; } if (_rsp_data[currentbyte] == CSAFE_GETCADENCE_CMD) { currentbyte++; datalength = _rsp_data[currentbyte]; currentbyte++; uint currentSPM = _rsp_data[currentbyte]; if ( currentSPM > 0) { _nSPM += currentSPM; _nSPMReads++; _strokeData.strokesPerMinute = currentSPM; _strokeData.strokesPerMinuteAverage = (double)_nSPM / (double)_nSPMReads; } currentbyte += datalength; } strokeDataUpdate(_strokeData); //Copy the part which was left out as begining int i2 = 0; if (_strokeData.forcePlotCount>=1) _strokeData.forcePlotCount-=1; for( int i= _strokeData.forcePlotCount;i<oldforcePlotCount;i++) { _strokeData.forcePlotPoints[i2] =_strokeData.forcePlotPoints[i]; i2++; } _strokeData.forcePlotCount = i2; }
void PM3Monitor::highResolutionUpdate() { _previousStrokePhase = _currentStrokePhase; initCommandBuffer(); // Get the stroke state. addCSafeData(CSAFE_SETUSERCFG1_CMD); addCSafeData(0x01); addCSafeData(CSAFE_PM_GET_STROKESTATE); executeCSafeCommand("highResolutionUpdate tkcmdsetCSAFE_command"); uint currentbyte = 0; uint datalength = 0; if (_rsp_data[currentbyte] == CSAFE_SETUSERCFG1_CMD) { currentbyte += 2; } if (_rsp_data[currentbyte] == CSAFE_PM_GET_STROKESTATE) { currentbyte++; datalength = _rsp_data[currentbyte]; currentbyte++; switch (_rsp_data[currentbyte]) { case 0: case 1: _currentStrokePhase = StrokePhase_Catch; break; case 2: _currentStrokePhase = StrokePhase_Drive; break; case 3: _currentStrokePhase = StrokePhase_Dwell; break; case 4: _currentStrokePhase = StrokePhase_Recovery; break; } currentbyte += datalength; } // Get any force curve points available. accumulateForceCurve(); if (_currentStrokePhase != _previousStrokePhase) { // If this is the dwell, complete the power curve. //if (_previousStrokePhase == StrokePhase_Drive) if (_currentStrokePhase == StrokePhase_Recovery) { lowResolutionUpdate(); } // Update the stroke phase. newStrokePhase(_currentStrokePhase); } }
/* Return RH and Temp using ICP1 capture, also return PV_IN (solar input voltage) and PWR (battery voltage). */ void Ht(void) { // only works if both edges are tracked and prescaler is set to CPU clock. if ( (icp1_edge_mode != TRACK_BOTH) || ( (TCCR1B & 0x07) != 0x01) ) { printf_P(PSTR("{\"err\":\"IcpWrongMode\"}\r\n")); initCommandBuffer(); return; } if ( (command_done == 10) ) { event_pair = 15; if ((event_pair < 1) || (event_pair >= (ICP_EVENT_BUFF_SIZE/2)) ) { printf_P(PSTR("{\"err\":\"IcpMaxArg%d\"}\r\n"),(ICP_EVENT_BUFF_SIZE/2)-1); initCommandBuffer(); } else { // event_pair should have a valid value for how many high-low capture pairs to print, but I need a counter to track up to it. event_pair_output =0; // buffer has enough readings if (icp1.count > ((event_pair * 2) +1) ) { uint8_t num_of_events_needed=((event_pair * 2) +1); // copy from icp1 to icp1_db double_buffer_copy(&icp1, &icp1_db, num_of_events_needed); // used to delay serial printing serial_print_started_at = millis(); command_done = 11; } else { printf_P(PSTR("{\"err\":\"IcpEvntCnt@%d\"}\r\n"), icp1.count); initCommandBuffer(); } } } else if ( (command_done == 11) ) { // use the event_count for indexing the time stamps sample_size = 0; low_sum = 0; high_sum = 0; printf_P(PSTR("{\"count\":\"%lu\","),icp1_db.count ); command_done = 12; } else if ( (command_done == 12) ) { // cast to int to use two's complement math then cast back into a uint8_t and mask to the buffer size. uint8_t high2low_event_index = ((uint8_t)(((int8_t)icp1_db.head) - ((int8_t)2*(event_pair_output)))) & (ICP_EVENT_BUFF_MASK); uint8_t low2high_event_index = ((uint8_t)(((int8_t)high2low_event_index) - 1)) & (ICP_EVENT_BUFF_MASK); uint8_t period_event_index = ((uint8_t)(((int8_t)low2high_event_index) - 1)) & (ICP_EVENT_BUFF_MASK); // the event time is keep in two byte arrays to make the ISR fast and // allow up to 32 events with quick access of the AVR ldd instruction. uint16_t high2low_event; uint16_t low2high_event; uint16_t period_event; high2low_event = (((uint16_t)icp1_db.event.Byt1[high2low_event_index]) <<8) + ((uint16_t)icp1_db.event.Byt0[high2low_event_index]); low2high_event = (((uint16_t)icp1_db.event.Byt1[low2high_event_index]) <<8) + ((uint16_t)icp1_db.event.Byt0[low2high_event_index]); period_event = (((uint16_t)icp1_db.event.Byt1[period_event_index]) <<8) + ((uint16_t)icp1_db.event.Byt0[period_event_index]); // Now find counts between events while ICP1 was low and then when it was high // cast to int causes two's complement math to be used which gives correct result through a roll over low = (uint16_t)((int16_t)high2low_event - (int16_t)low2high_event); high = (uint16_t)((int16_t)low2high_event - (int16_t)period_event); // if the last capture was on a falling event, swap low and high count if ( (icp1_db.event.status[high2low_event_index] & (1<<RISING)) == 0) { uint16_t temp = low; low = high; high = temp; } // the high and low timer counts are times between events // When HT is hot the high count can be less than 300 which may have some // skipping. This is due to the other ISR's running and not letting the capture // ISR do its job fast enough. A skip will cause the high count to be larger than the low. if ( high < low ) // this will not work bellow about -30C or -22F { sample_size += 1; low_sum += low; high_sum += high; } if ( (++event_pair_output) >= event_pair) { command_done = 13; } else { command_done = 12; } } else if ( (command_done == 13) ) { printf_P(PSTR("\"smpl_sz\":\"%u\""),(unsigned int)sample_size); if (sample_size > 10) { printf_P(PSTR(",")); command_done = 14; } else { command_done = 24; } } else if ( (command_done == 14) ) { // a fixed 1% resistor that discharges the timing capacitor, it does not very with temperature float R7 = 1500000.0; //R9 (an NTC thermistor) is measured from ratio of its charge rate and the R7 discharge rate float R9 = R7 * high_sum/low_sum; // Room Temp in K float RoomTemp = 25.0 + 273.15; // R9 is an NTC with Bata(K) 4582 float Beta = 4582.0; //R9 at RoomTemp (the actual value is within 5% of this) float R9rt = 470000.0; //sensor Temp in K sensor_temp = ((RoomTemp)*Beta/log(R9rt/R9))/(Beta/log(R9rt/R9)-RoomTemp); // print in deg F printf_P(PSTR("\"deg_f\":\"%1.2f\","),(sensor_temp - 273.15)*9/5 + 32); command_done = 15; } else if ( (command_done == 15) ) { // a fixed 1% resistor that discharges the timing capacitor, it does not very with temperature float R7 = 1500000.0; // Room Temp in K float RoomTemp = 25.0 + 273.15; // 555 IC supply float V555 = 3.58; // the voltage over which the ramp time occures, which is 1/3 of the 555 IC supply float V = V555/3; // see Robert A. Pease "What's All This VBE Stuff, Anyhow?" float Vbe = 0.675 - (0.00175 *(sensor_temp - RoomTemp) ); // the discharge current is mirrored from a 1.5Meg resistor (it is about 2uA at room temp) float i = (V555-Vbe)/R7; // sensor capacatance float C = i * (1.0/F_CPU) * low_sum/(sample_size*V); // C@55%RH, note I think the circuit adds to this e.g. 5pf or more. float Crt = 180.0*1E-12; // ratio used for reverse polynomial response of HS1101LF float X = C/Crt; // reverse polynomial response of HS1101LF from datasheet float RH = (-3.4656*1E3*X*X*X) + (1.0732*1E4*X*X) + (-1.0457*1E4*X) + (3.2459*1E3); printf_P(PSTR("\"%%RH\":\"%1.2f\","),RH); command_done = 16; } // The ADC values are from 0 to 1023 for 1024 slots where each reperesents 1/1024 of the reference. Last slot has issues // https://forum.arduino.cc/index.php?topic=303189.0 else if ( (command_done == 16) ) { // analog channel 6 is connected to the solar power input (PV_IN) float PV_IN = analogRead(6)*(5.0/1024.0)*(532.0/100.0); printf_P(PSTR("\"PV_IN\":\"%1.2f\""),PV_IN); command_done = 17; } else if ( (command_done == 17) ) { // analog channel 7 is connected to the battery power (PWR) float PWR = analogRead(7)*(5.0/1024.0)*(3.0/2.0); printf_P(PSTR("\"PWR\":\"%1.2f\""),PWR); command_done = 24; } else if ( (command_done == 24) ) { printf_P(PSTR("}\r\n")); command_done = 25; } else if ( (command_done == 25) ) { // delay between JSON printing unsigned long kRuntime= millis() - serial_print_started_at; if ((kRuntime) > ((unsigned long)SERIAL_PRINT_DELAY_MILSEC)) { command_done = 10; /* This keeps looping the output forever (until a Rx char anyway) */ } } else { printf_P(PSTR("{\"err\":\"IcpCmdDoneWTF\"}\r\n")); initCommandBuffer(); } }
int main(void) { /* Initialize UART, it returns a pointer to FILE so redirect of stdin and stdout works*/ stdout = stdin = uartstream0_init(BAUD); /* Initialize I2C, with the internal pull-up*/ twi_init(1); /* Clear and setup the command buffer, (probably not needed at this point) */ initCommandBuffer(); sei(); // Enable global interrupts char rpu_addr = get_Rpu_address(); // set a default address if RPU manager not found if (rpu_addr == 0) { rpu_addr = '0'; } while(1) { // check if character is available to assemble a command, e.g. non-blocking if ( (!command_done) && uart0_available() ) // command_done is an extern from parse.h { // get a character from stdin and use it to assemble a command AssembleCommand(getchar()); // address is a char e.g. the ascii value for '0' warning: a null will terminate the command string. StartEchoWhenAddressed(rpu_addr); } // check if the character is available, and if so stop transmit and the command in process. // a multi-drop bus can have another device start transmitting after the second received byte so // there is little time to detect a possible collision if ( command_done && uart0_available() ) { // dump the transmit buffer to limit a collision uart0_flush(); initCommandBuffer(); } // finish echo of the command line befor starting a reply (or the next part of reply) if ( command_done && (uart0_availableForWrite() == UART_TX0_BUFFER_SIZE) ) { if ( !echo_on ) { // this happons when the address did not match initCommandBuffer(); } else { // command is a pointer to string and arg[] is an array of pointers to strings // use findCommand to make them point to the correct places in the command line // this can only be done once, since spaces and delimeters are replaced with null termination if (command_done == 1) { findCommand(); command_done = 10; } if ( (command_done >= 10) && (command_done < 250) ) { ProcessCmd(); } else { initCommandBuffer(); } } } } return 0; }
/* return adc values */ void Analog(void) { if ( (command_done == 10) ) { // check that arguments are digit in the range 0..7 for (adc_arg_index=0; adc_arg_index < arg_count; adc_arg_index++) { if ( ( !( isdigit(arg[adc_arg_index][0]) ) ) || (atoi(arg[adc_arg_index]) < 0) || (atoi(arg[adc_arg_index]) > ADC_CHANNELS) ) { printf_P(PSTR("{\"err\":\"AdcChOutOfRng\"}\r\n")); initCommandBuffer(); return; } } // print in steps otherwise the serial buffer will fill and block the program from running serial_print_started_at = millis(); printf_P(PSTR("{")); adc_arg_index= 0; command_done = 11; } else if ( (command_done == 11) ) { // use the channel as an index in the JSON reply uint8_t arg_indx_channel =atoi(arg[adc_arg_index]); if (arg_indx_channel == 0) { printf_P(PSTR("\"ADC%s\":"),arg[adc_arg_index]); } if (arg_indx_channel == PV_I) //ADC1 { printf_P(PSTR("\"PV_A\":")); } if (arg_indx_channel == CHRG_I) //ADC2 { printf_P(PSTR("\"CHRG_A\":")); } if (arg_indx_channel == DISCHRG_I) //ADC3 { printf_P(PSTR("\"DISCHRG_A\":")); } if (arg_indx_channel == 4) { printf_P(PSTR("\"ADC4\":")); } if (arg_indx_channel == 5) { printf_P(PSTR("\"ADC5\":")); } if (arg_indx_channel == PV_V) //ADC6 { printf_P(PSTR("\"PV_V\":")); } if (arg_indx_channel == PWR_V) //ADC7 { printf_P(PSTR("\"PWR_V\":")); } command_done = 12; } else if ( (command_done == 12) ) { uint8_t arg_indx_channel =atoi(arg[adc_arg_index]); // There are values from 0 to 1023 for 1024 slots where each reperesents 1/1024 of the reference. Last slot has issues // https://forum.arduino.cc/index.php?topic=303189.0 if (arg_indx_channel == 0) { printf_P(PSTR("\"%1.2f\""),(analogRead(0)*5.0/1024.0)); } if (arg_indx_channel == PV_I) //CCtest board current sense that can be connected to ADC1. { printf_P(PSTR("\"%1.3f\""),(analogRead(PV_I)*(5.0/1024.0)/(0.068*50.0))); } if (arg_indx_channel == CHRG_I) // RPUno has ADC2 connected to high side current sense to measure battery charging. { printf_P(PSTR("\"%1.3f\""),(analogRead(CHRG_I)*(5.0/1024.0)/(0.068*50.0))); } if (arg_indx_channel == DISCHRG_I) // RPUno has ADC3 connected to high side current sense to measure battery discharg. { printf_P(PSTR("\"%1.3f\""),(analogRead(DISCHRG_I)*(5.0/1024.0)/(0.068*50.0))); } if (arg_indx_channel == 4) // RPUno has ADC4 is used for I2C SDA function { printf_P(PSTR("\"SDA\"")); } if (arg_indx_channel == 5) // RPUno has ADC5 is used for I2C SCL function { printf_P(PSTR("\"SCL\"")); } if (arg_indx_channel == PV_V) // RPUno has ADC6 connected to a voltage divider from the solar input. { printf_P(PSTR("\"%1.2f\""),(analogRead(PV_V)*(5.0/1024.0)*(532.0/100.0))); } if (arg_indx_channel == PWR_V) // RPUno has ADC7 connected a voltage divider from the battery (PWR). { printf_P(PSTR("\"%1.2f\""),(analogRead(PWR_V)*(5.0/1024.0)*(3.0/2.0))); } if ( (adc_arg_index+1) >= arg_count) { printf_P(PSTR("}\r\n")); // initCommandBuffer(); /* This stops the output after one loop*/ command_done = 13; } else { printf_P(PSTR(",")); adc_arg_index++; command_done = 11; } } else if ( (command_done == 13) ) { // delay between JSON printing unsigned long kRuntime= millis() - serial_print_started_at; if ((kRuntime) > ((unsigned long)SERIAL_PRINT_DELAY_MILSEC)) { command_done = 10; /* This keeps looping output forever (until a Rx char anyway) */ } } else { printf_P(PSTR("{\"err\":\"AdcCmdDoneWTF\"}\r\n")); initCommandBuffer(); } }
int main(void) { setup(); while(1) { // use LED to show if I2C has a bus manager blink(); // check if character is available to assemble a command, e.g. non-blocking if ( (!command_done) && uart0_available() ) // command_done is an extern from parse.h { // get a character from stdin and use it to assemble a command AssembleCommand(getchar()); // address is an ascii value, warning: a null address would terminate the command string. StartEchoWhenAddressed(rpu_addr); } // check if a character is available, and if so flush transmit buffer and nuke the command in process. // A multi-drop bus can have another device start transmitting after getting an address byte so // the first byte is used as a warning, it is the onlly chance to detect a possible collision. if ( command_done && uart0_available() ) { // dump the transmit buffer to limit a collision uart0_flush(); initCommandBuffer(); } // delay between ADC burst adc_burst(); // finish echo of the command line befor starting a reply (or the next part of a reply) if ( command_done && (uart0_availableForWrite() == UART_TX0_BUFFER_SIZE) ) { if ( !echo_on ) { // this happons when the address did not match initCommandBuffer(); } else { if (command_done == 1) { findCommand(); command_done = 10; } // do not overfill the serial buffer since that blocks looping, e.g. process a command in 32 byte chunks if ( (command_done >= 10) && (command_done < 250) ) { ProcessCmd(); } else { initCommandBuffer(); } } } } return 0; }
/* run reflow profile */ void Reflow(void) { if ( (command_done == 10) ) { eeprom_offset = 0; pwm =0; last_pwm =0; reflow_zone_started_at = millis(); pinMode(SSR, OUTPUT); digitalWrite(SSR, LOW); pinMode(BUZZER, OUTPUT); digitalWrite(BUZZER, LOW); command_done = 11; } else if ( (command_done == 11) ) { pwm = eeprom_read_byte( (uint8_t *) (eeprom_offset) ); printf_P(PSTR("{\"millis\":\"%lu\","),reflow_zone_started_at); // Turn on the SSR if pwm is between 1 thru 254 (255 is used for the buzzer) if ( (pwm > 0) && (pwm < 255) ) { digitalWrite(SSR, HIGH); } if (pwm == 255) { digitalWrite(BUZZER, HIGH); } command_done = 12; } else if ( (command_done == 12) ) { unsigned long now = millis(); unsigned long kRuntime= now - reflow_zone_started_at; if ( (kRuntime) > ( (unsigned long)( 0.5 + (pwm/255.0)*REFLOW_ZONE_DELAY_MILSEC) ) ) { digitalWrite(BUZZER, LOW); digitalWrite(SSR, LOW); printf_P(PSTR("\"pwm\":\"%u\","),pwm); command_done = 13; } else { command_done = 12; } } else if ( (command_done == 13) ) { // analog channel 0 may be connected to the Fluke 80TK Thermocouple Module set in deg F output float Thermocouple = analogRead(0)*1.0; // (1.1/1023.0)*(1000.0); printf_P(PSTR("\"deg_c\":\"%1.2f\""),(Thermocouple -32.0) * (5.0/9.0) ); command_done = 24; } else if ( (command_done == 24) ) { printf_P(PSTR("}\r\n")); command_done = 25; } else if ( (command_done == 25) ) { unsigned long now = millis(); unsigned long kRuntime= now - reflow_zone_started_at; if ((kRuntime) > ((unsigned long)REFLOW_ZONE_DELAY_MILSEC)) { if (eeprom_offset < EEPROM_SIZE) { if ( (pwm == 255) && (last_pwm == 255) ) { // two consecutive buzzer valuses terminate the profile. initCommandBuffer(); } else { eeprom_offset++; last_pwm = pwm; reflow_zone_started_at += REFLOW_ZONE_DELAY_MILSEC; /* advance start time an exact amount*/ command_done = 11; /* loop through all eeprom values */ } } else { initCommandBuffer(); } } } else { printf_P(PSTR("{\"err\":\"ReflowCmdWTF\"}\r\n")); initCommandBuffer(); } }
int main(void) { /* Initialize UART, it returns a pointer to FILE so redirect of stdin and stdout works*/ stdout = stdin = uartstream0_init(BAUD); initCommandBuffer(); sei(); // Enable global interrupts starts the UART // non-blocking code in loop while(1) { // check if character is available to assemble a command, e.g. non-blocking if ( (!command_done) && uart0_available() ) // command_done is an extern from parse.h { // get a character from stdin and use it to assemble a command AssembleCommand(getchar()); // address is the ascii value for '0' note: a null address will terminate the command string. StartEchoWhenAddressed('0'); } // check if the character is available, and if so stop transmit and the command in process. // a multi-drop bus can have another device start transmitting after the second received byte so // there is little time to detect a possible collision if ( command_done && uart0_available() ) { // dump the transmit buffer to limit a collision uart0_flush(); initCommandBuffer(); } // finish echo of the command line befor starting a reply (or the next part of reply), also non-blocking. if ( command_done && (uart0_availableForWrite() == UART_TX0_BUFFER_SIZE) ) { if ( !echo_on ) { // this happons when the address did not match initCommandBuffer(); } else { // command is a pointer to string and arg[] is an array of pointers to strings // use findCommand to make them point to the correct places in the command line // this can only be done once, since spaces and delimeters are replaced with null termination if (command_done == 1) { findCommand(); command_done = 2; } if (command_done == 2) { if (command != '\0' ) { printf_P(PSTR("{\"cmd\": \"%s\"}\r\n"),command); command_done = 3; } else { initCommandBuffer(); } } if (command_done == 3) { printf_P(PSTR("{\"arg_count\": \"%d\"}\r\n"),arg_count); if (arg_count > 0) { command_done = 4; } else { initCommandBuffer(); } } if (command_done == 4) { printf_P(PSTR("{\"arg[0]\": \"%s\"}\r\n"),arg[0]); if (arg_count > 1) { command_done = 5; } else { initCommandBuffer(); } } if (command_done == 5) { printf_P(PSTR("{\"arg[1]\": \"%s\"}\r\n"),arg[1]); if (arg_count > 2) { command_done = 6; } else { initCommandBuffer(); } } if (command_done == 6) { printf_P(PSTR("{\"arg[2]\": \"%s\"}\r\n"),arg[2]); if (arg_count > 3) { command_done = 7; } else { initCommandBuffer(); } } if (command_done == 7) { printf_P(PSTR("{\"arg[3]\": \"%s\"}\r\n"),arg[3]); if (arg_count > 4) { command_done = 8; } else { initCommandBuffer(); } } if (command_done == 8) { printf_P(PSTR("{\"arg[4]\": \"%s\"}\r\n"),arg[4]); initCommandBuffer(); } } } } return 0; }
int main(void) { // Initialize Timers, ADC, and clear bootloader, Arduino does these with init() in wiring.c initTimers(); //Timer0 Fast PWM mode, Timer1 & Timer2 Phase Correct PWM mode. init_ADC_single_conversion(EXTERNAL_AVCC); // warning AREF should only have a bypass cap init_uart0_after_bootloader(); // bootloader may have the UART setup // setup() // Set digital pins to control load init_load(); // Set digital pins to control solar init_pv(); // put ADC in free running Auto Trigger mode enable_ADC_auto_conversion(FREE_RUNNING); /* Initialize UART, it returns a pointer to FILE so redirect of stdin and stdout works*/ stdout = stdin = uartstream0_init(BAUD); /* Clear and setup the command buffer, (probably not needed at this point) */ initCommandBuffer(); sei(); // Enable global interrupts starts TIMER0, UART0, ADC and any other ISR's // this start up command should run cctest, e.g. after a reset. if (uart0_available() == 0) { strcpy_P(command_buf, PSTR("/0/cctest?")); command_done = 1; echo_on = 1; printf_P(PSTR("%s\r\n"), command_buf); } // loop() while(1) /* I am tyring to use non-blocking code */ { // check if character is available to assemble a command, e.g. non-blocking if ( (!command_done) && uart0_available() ) // command_done is an extern from parse.h { // get a character from stdin and use it to assemble a command AssembleCommand(getchar()); // address is the ascii value for '0' note: a null address will terminate the command string. StartEchoWhenAddressed('0'); } // check if a character is available, and if so flush transmit buffer and nuke the command in process. // A multi-drop bus can have another device start transmitting after getting an address byte so // the first byte is used as a warning, it is the onlly chance to detect a possible collision. if ( command_done && uart0_available() ) { // dump the transmit buffer to limit a collision uart0_flush(); initCommandBuffer(); //Enable the LT3652, which may have been turned off digitalWrite(SHUTDOWN, LOW); // trun off the load load_step(0); } // finish echo of the command line befor starting a reply (or the next part of a reply) if ( command_done && (uart0_availableForWrite() == UART_TX0_BUFFER_SIZE) ) { if ( !echo_on ) { // this happons when the address did not match initCommandBuffer(); } else { if (command_done == 1) { findCommand(); command_done = 10; } // do not overfill the serial buffer since that blocks looping, e.g. process a command in 32 byte chunks if ( (command_done >= 10) && (command_done < 250) ) { ProcessCmd(); } else { initCommandBuffer(); } } } } return 0; }
// find argument(s) starting from a given offset uint8_t findArgument(uint8_t at_command_buf_offset) { if (at_command_buf_offset < COMMAND_BUFFER_SIZE) { uint8_t lastAlphaNum = at_command_buf_offset; //get past any white space, but not end of line (EOL was replaced with a null) while (isspace(command_buf[lastAlphaNum]) && !(command_buf[lastAlphaNum] == '\0')) { lastAlphaNum++; } // after command+space but the char is null if( (command_buf[lastAlphaNum] == '\0') ) { if (echo_on) printf_P(PSTR("{\"err\": \"NullArgAftrCmd+Sp\"}\r\n")); initCommandBuffer(); return 0; } //for each valid argument add it to the arg array of strings for (arg_count = 0; command_buf[lastAlphaNum] != '\0' ; arg_count++) { // to many arguments if( !(arg_count < MAX_ARGUMENT_COUNT) ) { if (echo_on) printf_P(PSTR("{\"err\": \"ArgCnt%dAt%d\"}\r\n"), arg_count, lastAlphaNum); initCommandBuffer(); return 0; } arg[arg_count] = command_buf + lastAlphaNum; // skip through the argument while( (isalnum(command_buf[lastAlphaNum]) || (command_buf[lastAlphaNum] == '-')) && (lastAlphaNum < (COMMAND_BUFFER_SIZE-1)) ) { lastAlphaNum++; } if ( (command_buf[lastAlphaNum] == ARGUMNT_DELIMITER) ) { if ( lastAlphaNum < (COMMAND_BUFFER_SIZE-2) ) { // check if char after delimiter is valid for an arg if( !(isalnum(command_buf[lastAlphaNum+1]) || (command_buf[lastAlphaNum+1] == '-')) ) { if (echo_on) printf_P(PSTR("{\"err\": \"ArgAftr'%c@%d!Valid\"}\r\n"),command_buf[lastAlphaNum],lastAlphaNum); initCommandBuffer(); return 0; } // null terminate the argument, e.g. replace the delimiter command_buf[lastAlphaNum] = '\0'; lastAlphaNum++; } else { // a delimiter was found but there is not enough room for an argument and null termination if (echo_on) printf_P(PSTR("{\"err\": \"DropArgCmdLn2Lng\"}\r\n")); initCommandBuffer(); return 0; } } // only EOL or delimiter is valid way to terminate an argument (e.g. a space befor end of line is not valid) else if (command_buf[lastAlphaNum] != '\0') { // do not index past command buffer if (echo_on) printf_P(PSTR("{\"err\": \"!DelimAftrArg'%c@%d\"}\r\n"), command_buf[lastAlphaNum],lastAlphaNum); initCommandBuffer(); return 0; } } return arg_count; } else { // do not index past command buffer if (echo_on) printf_P(PSTR("{\"err\": \"ArgIndxPastCmdBuf\"}\r\n")); initCommandBuffer(); return 0; } }