commit 85386ff416463c5df002b8425eff229147a53845 Author: Fisch Date: Sat Apr 22 14:01:49 2023 +0200 add firmware from 2021-10-25 diff --git a/scalecontroller.ino b/scalecontroller.ino new file mode 100644 index 0000000..4141b41 --- /dev/null +++ b/scalecontroller.ino @@ -0,0 +1,1061 @@ +//use arduino 1.0.5, otherwise display library does not work properly + +#include +#include "HX711.h" +#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 +//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 + +long time_lastlcd=0; +long looptimelcd_margin=0; //if >0 loop took longer than expected +uint8_t fps=DEFAULT_FPS; //EEPROM +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=""; + +long time_lastadc=0; +long looptimeadc_margin=0; +uint8_t spsadc=DEFAULT_SPSADC; //eeprom +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 31 //needs to be uneven, maximum number, for array declaration +double weightseries[ADCMEDIANVALUES_MAX]; //last n values for median filter +int adcmedianvalues=DEFAULT_ADCMEDIANVALUES; //needs to be uneven //eeprom +int 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 setup() { + Serial.begin(9600); + + 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.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; + + + 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++; if (adcmedianposition>=adcmedianvalues) adcmedianposition=0; + 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; +} + +double 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 getWeightSeriesMin() +{ + double cmin=MAXDOUBLEVALUE; + for (int i=0;icmax) + cmax=weightseries[i]; + } + return cmax; +} + +String toWeightString(double w){ + return toWeightString(w,5,4); +} +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 (int i = 0; i < sizeof(value); i++) + EEPROM.write(ee++, *p++); +} + +double EEPROMReadDouble(int ee) +{ + double value = 0.0; + byte* p = (byte*)(void*)&value; + for (int 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++; +}