#include //use arduino 1.0.5, otherwise display library does not work properly #include #include #include #include #include #include #define EEPROMVERSION 1 Servo esc; #define PIN_BACK 8 #define PIN_DOWN 10 #define PIN_UP 11 #define PIN_SET 12 #define PIN_CURRENT A3 #define PIN_VOLTAGE A2 #define PIN_ESC 9 //D9 = PB1, pin 15 #define PIN_RPM 1 //Interrupt 0 = D2 = PD2, pin 4,Interrupt 1 = D3 = PD3, pin 5 uint16_t rotationcounter=0; //counts up on rotation interrupt uint16_t rpm=0; #define ESC_MIN 1000 #define ESC_MAX 2000 //2000 uint16_t esc_value=ESC_MIN; //esc_value uint8_t maxcurrent=10; uint8_t minvoltage=105; // *0.1 uint32_t btn_back_tdown=0; uint32_t btn_down_tdown=0; uint32_t btn_up_tdown=0; uint32_t btn_set_tdown=0; uint32_t btn_back_trelease=0; uint32_t btn_down_trelease=0; uint32_t btn_up_trelease=0; uint32_t btn_set_trelease=0; uint8_t btn_back_press=0; //0=not press, 1=down, 2=short press (on release), 3=long press(on release) uint8_t btn_down_press=0; uint8_t btn_up_press=0; uint8_t btn_set_press=0; #define BTN_BOUNCETIME 50 #define BTN_HOLDTIME 1000 //tutorial: set Multiplier to 1, apply known voltage/current, divide applied voltage/current by LCD Reading = multiplier #define VOLTAGEMULTIPLIER 0.02009 //0.0011792 //5*0.2415/1024 #define CURRENTMULTIPLIER 0.02083 // #define PIN_LED 13 #define MAXDOUBLEVALUE 32767 //DEFAULT EEPROM SETTINGS #define DEFAULT_FPS 5 #define DEFAULT_SCALECALIBRATION 5875 // (72500/12.34) <- rough estimation #define DEFAULT_ADCMEDIANVALUES 15 #define DEFAULT_SPSADC 10 // DOUT, SCK //HX711 scale(A1, A0); // parameter "gain" is ommited; the default value 128 is used by the library HX711 scale; //A+= green, A-=white, E-=Black, E+=Red LiquidCrystal_I2C lcd(0x27,16,2); // set the LCD address to 0x27 for a 16 chars and 2 line display //lcd sda an pin 27 (A4) //lcd sck an pin 28 (A5) double scalecalibration=DEFAULT_SCALECALIBRATION; //default //eeprom String SerialMessage = ""; String receivedString =""; char tempstring[16]; //for dtostrf unsigned long time_lastlcd=0; unsigned long looptimelcd_margin=0; //if >0 loop took longer than expected uint8_t fps=DEFAULT_FPS; //EEPROM unsigned long time_lcdwait=1000/fps; String lcd0=""; //lcd line 0 String lcd1=""; // " 1 #define LCD_BACKLIGHT_TIME 20000 uint32_t lcd_backlight_offtimer=0; #define LCD_BACKLIGHT_WEIGHT 2 //if weightchange lower than x, turn backlight off after LCD_BACKLIGHT_TIME ms String lcd0_buffer=""; String lcd1_buffer=""; unsigned long time_lastadc=0; unsigned long looptimeadc_margin=0; uint8_t spsadc=DEFAULT_SPSADC; //eeprom unsigned long time_adcwait=1000/spsadc; int adc_readings=1; //ca. 700ms for 5 readings, ca 75ms for 1 reading, readings=1 10times = 747ms double weight=0; #define ADCMEDIANVALUES_MAX 91 //needs to be uneven, maximum number, for array declaration float weightseries[ADCMEDIANVALUES_MAX]; //last n values for median filter uint16_t adcmedianvalues=DEFAULT_ADCMEDIANVALUES; //needs to be uneven //eeprom float adcFilterKeepMedianvaluesFactor=0.8; //how many lowest and highest values will be removed from the array before avaraging (as factor). 0.0 means only median is used. 1.0 means all values are used. uint16_t adcmedianposition=0; float calibrationWeight=100; //gramms #define N_CURRENTREADINGS 8 uint16_t currentreadings[N_CURRENTREADINGS]; uint8_t currentreading_pos=0; //STATES #define S_SCALEDISPLAY 0 #define S_MENU 1 uint8_t state_menu=0; #define SM_scale 0 #define SM_thrusttest 1 #define SM_calibration 2 #define SM_fps 3 #define SM_medianfilter 4 #define SM_spsadc 5 #define SM_maxcurrent 6 #define SM_minvoltage 7 #define SM_thrusttest_steptime 8 #define SM_save 9 #define SM_loaddefaults 10 String menu_entry[]={"Scale","Thrusttest","Calibration","FPS","Medianfilter","ADC Speed","Max A","Min V","Ramptime","Save","Load Defaults"}; #define MENU_ENTRIES 10 #define S_MENUENTRY 2 #define S_CALIBRATION 3 uint8_t state_calibration=0; //0=wait (for press), 1=measure, 2=show #define DELAY_CALIBRATIONWAIT 1000 //delay after key press for measuring #define DELAY_CALIBRATIONSHOW 5000 #define S_THRUSTDISPLAY 10 uint8_t state_thrusttest=0; unsigned long thrusttest_timer=0; #define THRUSTTEST_STARTDELAY 5000 uint8_t thrusttest_steptime=3; //time in ms for one step esc value. 1ms=>1sec from 0%-100%. --> time in seconds for 0-100% //EEPROM unsigned long thrusttest_holdmax=1000; //hold maximum value for x ms unsigned long thrusttest_waitend=5000; float test_maxamps=0; float test_maxthrust=0; unsigned long testtime=0; uint8_t state=S_SCALEDISPLAY; uint32_t statetimer=0; //for state delays void rpm_interrupt(); float StringToFloat(String s); void EEPROMWriteDouble(int ee, double value); double EEPROMReadDouble(int ee); uint16_t EEPROMReadInt(uint8_t paddr); void EEPROMWriteInt(uint8_t paddr,uint16_t pdata); void loadDefaults(); void writeEEPROMsettings(); void updateFPS(); void updateSPSADC(); void loadEEPROMsettings(); String toStringBar(double val,double minimum,double maximum); String toString(double w,uint8_t dec); String toString(double w); String toWeightString(double w,uint8_t dec,uint8_t len); String toWeightString(double w); float getWeightSeriesMax(); float getWeightSeriesMin(); double getWeightMedian(); double getWeightFiltered(); float getVoltage(); float getCurrent(); String menuentry_string(); void menuentry_set(uint8_t presstype); void menuentry_back(uint8_t presstype); void menuentry_up(uint8_t presstype); void menuentry_down(uint8_t presstype); void resetBTNFlag(uint8_t *btnpress); void buttonHandler(uint8_t pin,uint32_t *btntdown,uint32_t *btntrelease,uint8_t *btnpress); void buttonCheck(); void updateLCD(); void setup() { Serial.begin(115200); pinMode(PIN_LED, OUTPUT); digitalWrite(PIN_LED, HIGH); esc.attach(PIN_ESC,1000,2000); esc_value=ESC_MIN; esc.writeMicroseconds(esc_value); pinMode(PIN_UP, INPUT); digitalWrite(PIN_UP, HIGH); pinMode(PIN_DOWN, INPUT); digitalWrite(PIN_DOWN, HIGH); pinMode(PIN_SET, INPUT); digitalWrite(PIN_SET, HIGH); pinMode(PIN_BACK, INPUT); digitalWrite(PIN_BACK, HIGH); attachInterrupt(PIN_RPM, rpm_interrupt, RISING); lcd.init(); lcd.backlight(); lcd.noAutoscroll(); //Serial.println("Multiscale"); lcd.clear(); lcd.print("Multiscale"); if(!digitalRead(PIN_BACK)) //if back pressed, load defaults loadDefaults(); else loadEEPROMsettings(); //Serial.print("calib="); Serial.println(scalecalibration,10); lcd.setCursor(0,1); dtostrf(scalecalibration,4,5,tempstring); lcd.print("cal="+String(tempstring)); scale.begin(A1, A0); scale.set_scale(scalecalibration); scale.tare(); /*delay(500); lcd.clear(); long t_teststart=millis(); for (int i=0;i<10;i++) scale.get_units(1); long t_testend=millis(); dtostrf((t_testend-t_teststart),10,0,tempstring); lcd.print("t="+String(tempstring)); delay(2000);*/ digitalWrite(PIN_LED, LOW); lcd.clear(); } void loop() { buttonCheck(); //checkSerial(); /* if (receivedString.length()>0){ if (receivedString.equals("tare")){ double weightTareBefore=scale.get_units(5); scale.tare(); double weightTareAfter=scale.get_units(5); Serial.print("Tared. Difference="); Serial.println((weightTareAfter-weightTareBefore),4); } else if (receivedString.substring(0,11).equals("calibration")){ calibrationWeight=StringToFloat(receivedString.substring(12)+"0"); Serial.print("Calibration, set weight="); Serial.println(calibrationWeight,10); scale.set_scale(); scale.tare(); Serial.println("Reset Scale, place known weight now! ..."); delay(5000); Serial.println("Do not touch scale!"); double calibrationWeight_scale=scale.get_units(10); Serial.println(calibrationWeight_scale, 100); scalecalibration=calibrationWeight_scale/calibrationWeight; Serial.print("Done. Scalescalibration="); Serial.println(scalecalibration,10); scale.set_scale(scalecalibration); writeEEPROMsettings(); Serial.println("Saved to EEPROM"); }else if(receivedString.equals("g")){ Serial.println(scale.get_units(5), 10); }else{ Serial.print("unknown command: "); Serial.println(receivedString); } }*/ currentreadings[currentreading_pos]=analogRead(PIN_CURRENT); currentreading_pos++; currentreading_pos%=N_CURRENTREADINGS; double showweight=getWeightFiltered(); switch(state){ case S_SCALEDISPLAY: if ((getWeightSeriesMax()-getWeightSeriesMin())0) state_menu--; } if (btn_up_press==2){ if (state_menu<(MENU_ENTRIES-1)) state_menu++; } if (btn_set_press==2){ //Menu entry selected state=S_MENUENTRY; } break; case S_MENUENTRY: //menu entry selected lcd0=" #"+menu_entry[state_menu]+"#"; lcd1=menuentry_string(); //___ if (btn_back_press==2){ menuentry_back(btn_down_press); state=S_MENU; } if (btn_down_press>=2){ menuentry_down(btn_down_press); } if (btn_up_press>=2){ menuentry_up(btn_up_press); } if (btn_set_press>=2){ menuentry_set(btn_set_press); } break; case S_CALIBRATION: //menu entry selected if (btn_back_press==2){ state=S_MENUENTRY; //abort } if (state_calibration==0){ //wait for press lcd0="Remove weight"; lcd1="then press SET"; if (btn_set_press>=2){ state_calibration=1; statetimer=millis(); } }else if (state_calibration==1){ //taring if (millis()<(statetimer+DELAY_CALIBRATIONWAIT)){ //wait some time lcd0="Wait"; lcd1=""; }else{ lcd0="Taring"; lcd1=""; scale.set_scale(); scale.tare(); state_calibration=2; } }else if (state_calibration==2){ // wait for press lcd0="Place "+toString(calibrationWeight,0)+"g"; lcd1="press SET"; if (btn_set_press>=2){ state_calibration=3; statetimer=millis(); } }else if (state_calibration==3){ // measuring if (millis()<(statetimer+DELAY_CALIBRATIONWAIT)){ //wait some time lcd0="Please wait ..."; lcd1=""; }else if (millis()<(statetimer+DELAY_CALIBRATIONWAIT+500)){ //print new text lcd0=""; lcd1="Measuring ..."; }else{ double calibrationWeight_scale=scale.get_units(30); scalecalibration=calibrationWeight_scale/calibrationWeight; scale.set_scale(scalecalibration); writeEEPROMsettings(); state_calibration=4; statetimer=millis(); } }else if (state_calibration==4){ //show data if (millis()<(statetimer+DELAY_CALIBRATIONSHOW)){ //wait some time lcd0=""+toString(scalecalibration); lcd1="Saved to eeprom"; }else{ lcd0=""; lcd1=""; state_calibration=0; state=S_SCALEDISPLAY; } } break; case S_THRUSTDISPLAY: { if (state_thrusttest>0){ //only check if test started if (getCurrent()>maxcurrent ){ esc_value=ESC_MIN; esc.writeMicroseconds(esc_value); //esc off state_thrusttest=10; //10=amp max error } if (getVoltage()*10=2 && state_thrusttest<=4)){ lcd0=toWeightString(getCurrent(),1,2)+"A "+toWeightString(getVoltage(),2,1)+"V"; lcd1=toWeightString(weight,0,2)+"g " + String(esc_value)+" "+String(rpm); } if (state_thrusttest==0){ //wait to start //lcd0=toWeightString(getCurrent(),2,1)+"A "+toWeightString(getVoltage(),2,1)+"V"; //lcd1=toWeightString(weight,2,1)+"g v=" + toWeightString(esc_value,2,0); if (btn_up_press==3){ //long press scale.tare(); state_thrusttest=1; //start thrusttest_timer=millis(); } }else if (state_thrusttest==1){ //initialize thrusttest lcd0="Starting Test ..."; lcd1="Max "+toWeightString(maxcurrent,2,1)+"A"; esc_value=ESC_MIN; esc.writeMicroseconds(esc_value); if (millis()-thrusttest_timer>THRUSTTEST_STARTDELAY){ //start ramping Serial.println("Time;Value;Current;Voltage;Thrust;RPM"); //Print CSV Head test_maxamps=0; //reset statistics test_maxthrust=0; state_thrusttest=2; thrusttest_timer=millis(); //use timer for ramping testtime=millis(); } }else if (state_thrusttest==2){ //ramping up if (millis()-thrusttest_timer>thrusttest_steptime){ esc_value++; if (esc_value>ESC_MAX){ esc_value=ESC_MAX; state_thrusttest=3; //next state } esc.writeMicroseconds(esc_value); thrusttest_timer=millis(); } //lcd0=toWeightString(getCurrent(),2,1)+"A "+toWeightString(getVoltage(),2,1)+"V"; //lcd1=toWeightString(weight,2,1)+"g v=" + toWeightString(esc_value,2,0); }else if (state_thrusttest==3){ //hold if (millis()-thrusttest_timer>thrusttest_holdmax){ state_thrusttest=4; thrusttest_timer=millis(); } //lcd0=toWeightString(getCurrent(),2,1)+"A "+toWeightString(getVoltage(),2,1)+"V"; //lcd1=toWeightString(weight,2,1)+"g v=" + toWeightString(esc_value,2,0); }else if (state_thrusttest==4){ //ramp down if (millis()-thrusttest_timer>thrusttest_steptime){ esc_value--; if (esc_value= time_lastadc+time_adcwait) { weight=scale.get_units(adc_readings); adcmedianposition++; adcmedianposition%=adcmedianvalues; weightseries[adcmedianposition]=weight; //save weight to series for medianfilter looptimeadc_margin=millis()-time_lastadc-time_adcwait; uint16_t time_passed=millis()-time_lastadc; uint16_t _rotmultiplier=60000/time_passed; rpm=rotationcounter*_rotmultiplier; //rotationcounter*60000/(millis()-time_lastadc) rotationcounter=0; time_lastadc=millis(); if (state==S_THRUSTDISPLAY && state_thrusttest>=2 && state_thrusttest<=5){ //in thrusttest Serial.print(String(millis()-testtime)+";"); Serial.print(String(esc_value)+";"); Serial.print(toWeightString(getCurrent(),2,1)+";"); Serial.print(toWeightString(getVoltage(),2,1)+";"); Serial.print(toWeightString(weight,2,1)+";"); Serial.println(String(rpm)); } } //DISPLAY if (millis() >= time_lastlcd+time_lcdwait) { updateLCD(); looptimelcd_margin=millis()-time_lastlcd-time_lcdwait; time_lastlcd=millis(); } //Status led lights up if atmega cannot keep up if (looptimelcd_margin>time_lcdwait || looptimeadc_margin>time_adcwait){ digitalWrite(PIN_LED,HIGH); }else{ digitalWrite(PIN_LED,LOW); } } void updateLCD(){ /*switch(state){ case S_SCALEDISPLAY: if ((getWeightSeriesMax()-getWeightSeriesMin())millis()){ //short *btnpress=2; }else{ //long *btnpress=3; } } if ((*btntdown+BTN_BOUNCETIME)0) calibrationWeight--; }else if(presstype==3){ if (calibrationWeight>50) calibrationWeight-=50; else calibrationWeight=0; } break; case SM_fps: if (fps>1) fps--; break; case SM_medianfilter: if (adcmedianvalues>3) adcmedianvalues-=2; break; case SM_spsadc: if (spsadc>1) spsadc--; break; case SM_maxcurrent: if (maxcurrent>1) maxcurrent--; break; case SM_minvoltage: if (minvoltage>1) minvoltage--; break; case SM_thrusttest_steptime: if (thrusttest_steptime>1) thrusttest_steptime--; break; } } void menuentry_up(uint8_t presstype){ //UP switch(state_menu){ case SM_calibration: // Calibration if (presstype==2) calibrationWeight++; if (presstype==3) calibrationWeight+=50; break; case SM_fps: if (fps<25) fps++; break; case SM_medianfilter: if (adcmedianvalues<(ADCMEDIANVALUES_MAX-1)) adcmedianvalues+=2; break; case SM_spsadc: if (spsadc<100) spsadc++; break; case SM_maxcurrent: if (maxcurrent<50) maxcurrent++; break; case SM_minvoltage: if (minvoltage<255) minvoltage++; break; case SM_thrusttest_steptime: if (thrusttest_steptime<255) thrusttest_steptime++; break; } } void menuentry_back(uint8_t presstype){ //BACK, only additional function switch(state_menu){ case SM_fps: // FPS updateFPS(); //calculate waittime from fps variable break; case SM_spsadc: updateSPSADC(); //calculate waittime state=S_MENU; break; } } void menuentry_set(uint8_t presstype){ //SET switch(state_menu){ case SM_scale: //Scale state=S_SCALEDISPLAY; //switch to scale display break; case SM_thrusttest: //Thrusttest state=S_THRUSTDISPLAY; lcd.backlight(); break; case SM_calibration: // Calibration state=S_CALIBRATION; state_calibration=0; break; case SM_fps: // FPS updateFPS(); //calculate waittime from fps variable state=S_MENU; break; case SM_medianfilter: // medianfilter state=S_MENU; break; case SM_spsadc: updateSPSADC(); //calculate waittime state=S_MENU; break; case SM_save: // Save EEPROM writeEEPROMsettings(); state=S_MENU; break; case SM_loaddefaults: // Load defaults loadDefaults(); state=S_MENU; break; } } String menuentry_string(){ //second line string if entry selected String s=""; switch(state_menu){ case SM_scale: //Scale case SM_thrusttest: case SM_save: case SM_loaddefaults: s="Press SET"; break; case SM_calibration: // Calibration s=toString(calibrationWeight,0)+"g"; break; case SM_fps: s=toString(fps,0); break; case SM_medianfilter: s=toString(adcmedianvalues,0); break; case SM_spsadc: s=toString(spsadc,0)+"Hz"; break; case SM_maxcurrent: s=toString(maxcurrent,0)+"A"; break; case SM_minvoltage: s=toString(minvoltage/10.0,1)+"V"; break; case SM_thrusttest_steptime: s=toString(thrusttest_steptime,0)+"s"; break; } return s; } float getCurrent() { uint16_t _current=0; for (uint8_t i=0;icmax){ cmax=weightseries[i]; cmax_i=i; } } } mask[cmin_i]=true; mask[cmax_i]=true; } return cmax; } double getWeightFiltered() { boolean mask[adcmedianvalues]; //true=disabled value for (uint16_t i=0;i2 && _valuesLeft>(uint16_t)(adcmedianvalues*adcFilterKeepMedianvaluesFactor)) { cmin=MAXDOUBLEVALUE; cmax=-MAXDOUBLEVALUE-1; for (uint16_t i=0;icmax){ cmax=weightseries[i]; cmax_i=i; } } } mask[cmin_i]=true; mask[cmax_i]=true; _valuesLeft-=2; //2 values have been masked out } double sum=0; uint16_t meanvalues=0; for (uint16_t i=0;icmax) cmax=weightseries[i]; } return cmax; } String toWeightString(double w){ return toWeightString(w,3,6); } String toWeightString(double w,uint8_t dec,uint8_t len){ char outstring[16]; char vz; if(w<0) { vz='-'; }else{ vz='+'; } dtostrf(abs(w),len,dec,outstring); return String(vz)+""+String(outstring); } String toString(double w){ return toString(w,5); } String toString(double w,uint8_t dec){ char outstring[16]; dtostrf(w,1,dec,outstring); return String(outstring); } String toStringBar(double val,double minimum,double maximum){ String s=""; if (valmaximum){ s=" >"; }else{ uint8_t pos=map(val,minimum,maximum,0,15); for (uint8_t i=0;i0) receivedString=SerialMessage; //Save the string bevore clearing SerialMessage=""; Serial.println(); } else // For all other characters, { char c=Serial.read(); SerialMessage.concat(c); //concat all received characters Serial.print(c); } } }*/ void updateFPS(){ time_lcdwait=1000/fps; } void updateSPSADC(){ time_adcwait=1000/spsadc; } void loadEEPROMsettings() //load diversity settings { uint8_t addr=0; uint8_t checkbyte=EEPROM.read(addr++); //checkbyte //0 if (checkbyte==EEPROMVERSION){ //if checkbyte correct scalecalibration=EEPROMReadDouble(addr); addr+=4; //uint32_t fps=EEPROM.read(addr++); //uint8_t adcmedianvalues=EEPROM.read(addr++); //uint8_t spsadc=EEPROM.read(addr++); //uint8_t maxcurrent=EEPROM.read(addr++); //uint8_t minvoltage=EEPROM.read(addr++); //uint8_t thrusttest_steptime=EEPROM.read(addr++); //uint8_t updateFPS(); updateSPSADC(); }else{ Serial.println("Wrong EEPROM Vers."); } } void writeEEPROMsettings() //save diversity settings { uint8_t addr=0; EEPROM.write(addr++,EEPROMVERSION); EEPROMWriteDouble(addr, scalecalibration); addr+=4; EEPROM.write(addr++, fps); EEPROM.write(addr++, adcmedianvalues); EEPROM.write(addr++, spsadc); EEPROM.write(addr++, maxcurrent); EEPROM.write(addr++, minvoltage); EEPROM.write(addr++, thrusttest_steptime); //EEPROM.write(addr++, scalecalibration); //EEPROMWriteInt(addr+=2, switchdeadtime); } void loadDefaults(){ scalecalibration=DEFAULT_SCALECALIBRATION; fps=DEFAULT_FPS; updateFPS(); adcmedianvalues=DEFAULT_ADCMEDIANVALUES; spsadc=DEFAULT_SPSADC; updateSPSADC(); } void EEPROMWriteInt(uint8_t paddr,uint16_t pdata){ EEPROM.write(paddr, (uint8_t)(pdata%256)); EEPROM.write(++paddr, (uint8_t)(pdata/256)); } uint16_t EEPROMReadInt(uint8_t paddr){ uint16_t data=EEPROM.read(paddr); data+=EEPROM.read(++paddr)*256; return data; } void EEPROMWriteDouble(int ee, double value) { byte* p = (byte*)(void*)&value; for (uint16_t i = 0; i < sizeof(value); i++) EEPROM.write(ee++, *p++); } double EEPROMReadDouble(int ee) { double value = 0.0; byte* p = (byte*)(void*)&value; for (uint16_t i = 0; i < sizeof(value); i++) *p++ = EEPROM.read(ee++); return value; } float StringToFloat(String s){ char buf[s.length()]; s.toCharArray(buf,s.length()); return atof(buf); } void rpm_interrupt(){ rotationcounter++; }