2023-04-22 12:02:51 +00:00
# include <Arduino.h>
2023-04-22 12:01:49 +00:00
//use arduino 1.0.5, otherwise display library does not work properly
# include <Servo.h>
2023-04-22 12:02:51 +00:00
# include <HX711.h>
2023-04-22 12:01:49 +00:00
# include <EEPROM.h>
# include <Wire.h>
# include <LiquidCrystal_I2C.h>
2023-04-22 12:49:37 +00:00
# include <ArduinoSort.h>
2023-04-22 12:01:49 +00:00
# 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
2023-04-22 12:02:51 +00:00
//HX711 scale(A1, A0); // parameter "gain" is ommited; the default value 128 is used by the library
HX711 scale ;
2023-04-22 12:01:49 +00:00
//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
2023-04-22 12:49:37 +00:00
unsigned long time_lastlcd = 0 ;
unsigned long looptimelcd_margin = 0 ; //if >0 loop took longer than expected
2023-04-22 12:01:49 +00:00
uint8_t fps = DEFAULT_FPS ; //EEPROM
2023-04-22 12:49:37 +00:00
unsigned long time_lcdwait = 1000 / fps ;
2023-04-22 12:01:49 +00:00
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 = " " ;
2023-04-22 12:49:37 +00:00
unsigned long time_lastadc = 0 ;
unsigned long looptimeadc_margin = 0 ;
2023-04-22 12:01:49 +00:00
uint8_t spsadc = DEFAULT_SPSADC ; //eeprom
2023-04-22 12:49:37 +00:00
unsigned long time_adcwait = 1000 / spsadc ;
2023-04-22 12:01:49 +00:00
int adc_readings = 1 ; //ca. 700ms for 5 readings, ca 75ms for 1 reading, readings=1 10times = 747ms
double weight = 0 ;
2023-04-22 13:15:08 +00:00
# define ADCMEDIANVALUES_MAX 93 //needs to be uneven, maximum number, for array declaration
2023-04-22 12:49:37 +00:00
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 ;
2023-04-22 12:01:49 +00:00
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
2023-04-22 12:02:51 +00:00
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 ) ;
2023-04-22 12:49:37 +00:00
float getWeightSeriesMax ( ) ;
float getWeightSeriesMin ( ) ;
2023-04-22 12:02:51 +00:00
double getWeightMedian ( ) ;
2023-04-22 12:49:37 +00:00
double getWeightFiltered ( ) ;
float getVoltage ( ) ;
float getCurrent ( ) ;
2023-04-22 12:02:51 +00:00
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 ( ) ;
2023-04-22 12:01:49 +00:00
void setup ( ) {
2023-04-22 13:15:08 +00:00
Serial . begin ( 115200 ) ;
2023-04-22 12:01:49 +00:00
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 ) ) ;
2023-04-22 12:02:51 +00:00
scale . begin ( A1 , A0 ) ;
2023-04-22 12:01:49 +00:00
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 ;
2023-04-22 13:15:08 +00:00
double showweight = getWeightFiltered ( ) ;
2023-04-22 12:01:49 +00:00
switch ( state ) {
case S_SCALEDISPLAY :
if ( ( getWeightSeriesMax ( ) - getWeightSeriesMin ( ) ) < LCD_BACKLIGHT_WEIGHT ) {
if ( lcd_backlight_offtimer = = 0 )
lcd_backlight_offtimer = millis ( ) ;
if ( ( lcd_backlight_offtimer + LCD_BACKLIGHT_TIME ) < millis ( ) )
lcd . noBacklight ( ) ;
} else {
lcd_backlight_offtimer = 0 ;
lcd . backlight ( ) ;
}
lcd0 = toStringBar ( weight , 0 , 1000 ) ;
//lcd1=toWeightString(weight)+"g";
2023-04-22 12:49:37 +00:00
//lcd1=toWeightString(getWeightMedian())+"g";
2023-04-22 13:15:08 +00:00
lcd1 = toWeightString ( showweight ) + " g " ;
Serial . print ( showweight , 3 ) ; Serial . println ( " g " ) ;
2023-04-22 12:01:49 +00:00
//___
if ( btn_back_press = = 2 ) { //press BACK to tare
scale . tare ( ) ;
}
if ( btn_set_press = = 2 ) {
state = S_MENU ;
state_menu = 0 ;
}
break ;
case S_MENU :
lcd . backlight ( ) ;
lcd0 = menu_entry [ state_menu ] ;
lcd1 = " --- " ;
//___
if ( btn_back_press = = 2 ) {
state = S_SCALEDISPLAY ;
}
if ( btn_down_press = = 2 ) {
if ( state_menu > 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 < minvoltage ) {
esc_value = ESC_MIN ;
esc . writeMicroseconds ( esc_value ) ; //esc off
state_thrusttest = 11 ; //11=undervoltage
}
}
if ( btn_down_press = = 1 | | btn_back_press = = 1 ) {
esc_value = ESC_MIN ;
esc . writeMicroseconds ( esc_value ) ; //esc off
state_thrusttest = 0 ;
}
if ( btn_set_press = = 2 ) { //goto menu
state_thrusttest = 0 ;
esc_value = ESC_MIN ;
esc . writeMicroseconds ( esc_value ) ; //esc off
state = S_MENU ;
state_menu = 0 ;
}
//look for new max values
if ( test_maxamps < getCurrent ( ) ) {
test_maxamps = getCurrent ( ) ;
}
if ( test_maxthrust < weight ) {
test_maxthrust = weight ;
}
if ( state_thrusttest = = 0 | | ( state_thrusttest > = 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 < ESC_MIN ) {
esc_value = ESC_MIN ;
state_thrusttest = 5 ; //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 = = 5 ) { //end, wait
if ( millis ( ) - thrusttest_timer > thrusttest_waitend ) {
state_thrusttest = 0 ;
esc_value = ESC_MIN ;
esc . writeMicroseconds ( esc_value ) ; //make shure esc is off
thrusttest_timer = millis ( ) ;
}
lcd0 = " Max " + toWeightString ( test_maxamps , 2 , 1 ) + " A " ;
lcd1 = toWeightString ( test_maxthrust , 2 , 1 ) + " g " ;
} else if ( state_thrusttest = = 10 ) { //amp max error
lcd0 = " Max Amp " ;
} else if ( state_thrusttest = = 11 ) { //min voltage error
lcd0 = " Undervolt. " ;
}
break ;
}
}
//reset button flags
resetBTNFlag ( & btn_back_press ) ;
resetBTNFlag ( & btn_down_press ) ;
resetBTNFlag ( & btn_up_press ) ;
resetBTNFlag ( & btn_set_press ) ;
//read scale
if ( millis ( ) > = time_lastadc + time_adcwait )
{
weight = scale . get_units ( adc_readings ) ;
2023-04-22 13:15:08 +00:00
adcmedianposition + + ;
adcmedianposition % = adcmedianvalues ;
2023-04-22 12:01:49 +00:00
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 ( ) {
2023-04-22 12:02:51 +00:00
2023-04-22 12:01:49 +00:00
/*switch(state){
case S_SCALEDISPLAY :
if ( ( getWeightSeriesMax ( ) - getWeightSeriesMin ( ) ) < LCD_BACKLIGHT_WEIGHT ) {
if ( lcd_backlight_offtimer = = 0 )
lcd_backlight_offtimer = millis ( ) ;
if ( ( lcd_backlight_offtimer + LCD_BACKLIGHT_TIME ) < millis ( ) )
lcd . noBacklight ( ) ;
} else {
lcd_backlight_offtimer = 0 ;
lcd . backlight ( ) ;
}
lcd0 = toStringBar ( weight , 0 , 500 ) ;
lcd1 = toWeightString ( weight ) + " g " ;
break ;
case S_MENU : //entry show
lcd . backlight ( ) ;
lcd0 = menu_entry [ state_menu ] ;
lcd1 = " --- " ;
break ;
case S_MENUENTRY : //Entry selected/entered
lcd0 = " # " + menu_entry [ state_menu ] + " # " ;
lcd1 = menuentry_string ( ) ;
break ;
case S_CALIBRATION :
if ( state_calibration = = 0 ) { //wait for press
lcd0 = " Place weight and " ;
lcd1 = " press SET " ;
} else if ( state_calibration = = 1 ) { //measuring
lcd0 = " Please wait ... " ;
lcd1 = " " ;
} else if ( state_calibration = = 2 ) {
lcd0 = " " ;
lcd1 = " press SET " ;
}
break ;
}
*/
if ( ! lcd0 . equals ( lcd0_buffer ) | | ! lcd1 . equals ( lcd1_buffer ) ) {
lcd . clear ( ) ;
lcd . print ( lcd0 ) ;
lcd . setCursor ( 0 , 1 ) ;
lcd . print ( lcd1 ) ;
lcd0_buffer = lcd0 ;
lcd1_buffer = lcd1 ;
}
}
void buttonCheck ( )
{
buttonHandler ( PIN_BACK , & btn_back_tdown , & btn_back_trelease , & btn_back_press ) ;
buttonHandler ( PIN_DOWN , & btn_down_tdown , & btn_down_trelease , & btn_down_press ) ;
buttonHandler ( PIN_UP , & btn_up_tdown , & btn_up_trelease , & btn_up_press ) ;
buttonHandler ( PIN_SET , & btn_set_tdown , & btn_set_trelease , & btn_set_press ) ;
}
void buttonHandler ( uint8_t pin , uint32_t * btntdown , uint32_t * btntrelease , uint8_t * btnpress )
{
if ( ! digitalRead ( pin ) ) {
if ( * btntdown = = 0 & & ( * btntrelease + BTN_BOUNCETIME ) < millis ( ) ) {
* btntdown = millis ( ) ;
* btnpress = 1 ; //is down
}
} else {
if ( * btntdown ! = 0 ) {
if ( * btnpress = = 1 ) { //if down press not already handled, ie set to 0
if ( ( * btntdown + BTN_HOLDTIME ) > millis ( ) ) { //short
* btnpress = 2 ;
} else { //long
* btnpress = 3 ;
}
}
if ( ( * btntdown + BTN_BOUNCETIME ) < millis ( ) ) { //wait if debouncetime over
* btntdown = 0 ;
* btntrelease = millis ( ) ;
}
}
}
}
2023-04-22 12:02:51 +00:00
2023-04-22 12:01:49 +00:00
void resetBTNFlag ( uint8_t * btnpress ) {
if ( * btnpress = = 2 | | * btnpress = = 3 )
* btnpress = 0 ; //reset if press or hold
}
void menuentry_down ( uint8_t presstype ) { //DOWN
switch ( state_menu ) {
case SM_calibration : // Calibration
if ( presstype = = 2 ) {
if ( calibrationWeight > 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 ;
}
}
2023-04-22 12:02:51 +00:00
2023-04-22 12:01:49 +00:00
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 ;
}
}
2023-04-22 12:02:51 +00:00
2023-04-22 12:01:49 +00:00
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 ;
}
2023-04-22 12:49:37 +00:00
float getCurrent ( )
2023-04-22 12:01:49 +00:00
{
uint16_t _current = 0 ;
for ( uint8_t i = 0 ; i < N_CURRENTREADINGS ; i + + ) {
_current + = currentreadings [ i ] / N_CURRENTREADINGS ;
}
return _current * CURRENTMULTIPLIER + 0.00001 ;
}
2023-04-22 12:49:37 +00:00
float getVoltage ( )
2023-04-22 12:01:49 +00:00
{
return analogRead ( PIN_VOLTAGE ) * VOLTAGEMULTIPLIER + 0.00001 ;
}
double getWeightMedian ( ) //return median weight from weightseries
{
boolean mask [ adcmedianvalues ] ; //true=disabled value
2023-04-22 12:49:37 +00:00
for ( uint16_t i = 0 ; i < adcmedianvalues ; i + + )
2023-04-22 12:01:49 +00:00
mask [ i ] = false ;
2023-04-22 12:49:37 +00:00
float cmin = MAXDOUBLEVALUE ; uint16_t cmin_i = 0 ;
float cmax = 0 ; uint16_t cmax_i = 0 ;
2023-04-22 12:01:49 +00:00
while ( cmin ! = cmax ) { //stop when only one value left
cmin = MAXDOUBLEVALUE ;
cmax = - MAXDOUBLEVALUE - 1 ;
2023-04-22 12:49:37 +00:00
for ( uint16_t i = 0 ; i < adcmedianvalues ; i + + ) {
2023-04-22 12:01:49 +00:00
if ( ! mask [ i ] ) {
if ( weightseries [ i ] < cmin ) {
cmin = weightseries [ i ] ;
cmin_i = i ;
}
if ( weightseries [ i ] > cmax ) {
cmax = weightseries [ i ] ;
cmax_i = i ;
}
}
}
mask [ cmin_i ] = true ;
mask [ cmax_i ] = true ;
}
return cmax ;
}
2023-04-22 12:49:37 +00:00
double getWeightFiltered ( ) {
boolean mask [ adcmedianvalues ] ; //true=disabled value
for ( uint16_t i = 0 ; i < adcmedianvalues ; i + + )
mask [ i ] = false ;
float cmin = MAXDOUBLEVALUE ; uint16_t cmin_i = 0 ;
float cmax = 0 ; uint16_t cmax_i = 0 ;
//while(cmin!=cmax){ //stop when only one value left
uint16_t _valuesLeft = adcmedianvalues ;
while ( _valuesLeft > 2 & & _valuesLeft > ( uint16_t ) ( adcmedianvalues * adcFilterKeepMedianvaluesFactor ) )
{
cmin = MAXDOUBLEVALUE ;
cmax = - MAXDOUBLEVALUE - 1 ;
for ( uint16_t i = 0 ; i < adcmedianvalues ; i + + ) {
if ( ! mask [ i ] ) {
if ( weightseries [ i ] < cmin ) {
cmin = weightseries [ i ] ;
cmin_i = i ;
}
if ( weightseries [ i ] > cmax ) {
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 ; i < adcmedianvalues ; i + + ) {
if ( ! mask [ i ] ) { //not masked out
sum + = weightseries [ i ] ;
meanvalues + + ;
}
}
return sum / meanvalues ;
}
float getWeightSeriesMin ( )
2023-04-22 12:01:49 +00:00
{
2023-04-22 12:49:37 +00:00
float cmin = MAXDOUBLEVALUE ;
for ( uint16_t i = 0 ; i < adcmedianvalues ; i + + ) {
2023-04-22 12:01:49 +00:00
if ( weightseries [ i ] < cmin )
cmin = weightseries [ i ] ;
}
return cmin ;
}
2023-04-22 12:49:37 +00:00
float getWeightSeriesMax ( )
2023-04-22 12:01:49 +00:00
{
2023-04-22 12:49:37 +00:00
float cmax = 0 ;
for ( uint16_t i = 0 ; i < adcmedianvalues ; i + + ) {
2023-04-22 12:01:49 +00:00
if ( weightseries [ i ] > cmax )
cmax = weightseries [ i ] ;
}
return cmax ;
}
String toWeightString ( double w ) {
2023-04-22 13:15:08 +00:00
return toWeightString ( w , 2 , 6 ) ;
2023-04-22 12:01:49 +00:00
}
String toWeightString ( double w , uint8_t dec , uint8_t len ) {
char outstring [ 16 ] ;
char vz ;
2023-04-22 13:15:08 +00:00
if ( w < 0 ) {
vz = ' - ' ;
} else {
vz = ' + ' ;
}
2023-04-22 12:01:49 +00:00
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 ) ;
}
2023-04-22 12:02:51 +00:00
2023-04-22 12:01:49 +00:00
String toStringBar ( double val , double minimum , double maximum ) {
String s = " " ;
if ( val < minimum ) {
s = " < " ;
} else if ( val > maximum ) {
s = " > " ;
} else {
uint8_t pos = map ( val , minimum , maximum , 0 , 15 ) ;
for ( uint8_t i = 0 ; i < pos ; i + + ) {
s + = " " ;
}
s + = " | " ;
}
return s ;
}
2023-04-22 12:02:51 +00:00
2023-04-22 12:01:49 +00:00
/*
void checkSerial ( ) {
receivedString = " " ;
while ( Serial . available ( ) )
{
if ( Serial . peek ( ) = = ' \n ' | | Serial . peek ( ) = = ' \r ' ) // Ignore newlines.
{
Serial . read ( ) ;
if ( SerialMessage . length ( ) > 0 )
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);
}
2023-04-22 12:02:51 +00:00
2023-04-22 12:01:49 +00:00
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 ;
2023-04-22 12:49:37 +00:00
for ( uint16_t i = 0 ; i < sizeof ( value ) ; i + + )
2023-04-22 12:01:49 +00:00
EEPROM . write ( ee + + , * p + + ) ;
}
double EEPROMReadDouble ( int ee )
{
double value = 0.0 ;
byte * p = ( byte * ) ( void * ) & value ;
2023-04-22 12:49:37 +00:00
for ( uint16_t i = 0 ; i < sizeof ( value ) ; i + + )
2023-04-22 12:01:49 +00:00
* 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 + + ;
2023-04-22 12:02:51 +00:00
}