This paste brought to you by Pastebin. View Raw

  1. // CG-scale originally designed and published by Olav Kallhovd; https://github.com/olkal/CG_scale
  2. // based on Aaro Malila, Finland, 2017 (aaro.malila@gmail.com) code
  3.  
  4. // Upraveno 09/2021, Pavel Dvořák (PAD73)
  5. // Pro správnou funkci musí být použity správné knihovny. Jedná se především o knihovnu pro displej New-LiquidCrystal-master.zip
  6. // a pro převodníky HX711 HX711-master.zip.
  7.  
  8. /*
  9. Connections / pins:  
  10.  - rear load cell:   A0-A1
  11.  - front load cell:  A2-A3
  12.  - LCD i2c-bus:      A4-A5 (SDA-SCL)
  13.  - battery voltage   A6
  14.  
  15. Calibration function
  16. It is possible to set the calibration factors with 3 buttons. A known calibration weight is needed.
  17.  
  18. Altered values are saved to EEPROM.
  19.  - navigation buttons / pins
  20.     * btn 1 digital2 func
  21.     * btn 2 digital3 -
  22.     * btn 3 digital4 +
  23.  
  24. Hold btn 1 down a few seconds and release it to enter calibration menu. Select sensor and steps by clicking btn1, and
  25. make adjustments to scale factor by clicking buttons 2 and 3 until the weight is what it is supposed to be. Go through
  26. all the steps, after the last one values are written to EEPROM.
  27.  
  28. Led or beeper is used to signal if a) battery is low or b) there is no activity after certain time, scale being in
  29. power save state and LCD backlight shut off.
  30.  
  31. Voltage divider with R1=10 R2=1 kohm resistors are used.
  32.  
  33. Operating cycle is defined ONLY by how fast the sensors give the measurements. Quite slow
  34. amplifiers were used. If faster ones are found, it might be necessary to add some delay()
  35. to the end of the loop -block.
  36.  
  37. HX711 amplifiers and LCD seem to consume quite a lot. This is why update intervals are changed during the operation.
  38.  
  39. */
  40.  
  41. //libraries
  42. //display
  43. #include <Wire.h> //for i2c-bus
  44. #include <LiquidCrystal_I2C.h>
  45.  
  46. //load cell amplifiers
  47. #include "HX711.h"
  48.  
  49.  
  50. //load cell amplifiers
  51. HX711 front_scale;
  52. HX711 rear_scale;
  53.  
  54.  
  55. //eeprom
  56. #include <EEPROM.h>
  57.  
  58. //pins
  59. byte batRefPin = A6; //battery voltage measurement
  60. byte button1Pin = 2;
  61. byte button2Pin = 3;
  62. byte button3Pin = 4;
  63. byte beeperPin  = 5;
  64.  
  65. //variable
  66. float battValue; //battery voltage
  67. float eW,tW; //measurement results from front/rear load cells
  68. float CGratio; //a ratio that is calculated from values above...
  69. float CG; //final result
  70.  
  71. //physical dimensions (distances), make sure that these are coherent with the mechanical unit
  72. float WingPegDist = 120; // - calibration value in mm, projected distance between wing support points, measure with calliper
  73. float LEstopperDist = 30.0; // -calibration value in mm, projected distance from front wing support point to leading edge (stopper pin), measure with calliper
  74.  
  75. float CGoffset = ((WingPegDist / 2) + LEstopperDist);
  76.  
  77. //Lsome stuff for LCD
  78. #define I2C_ADDR    0x27 // 0x3f Display's i2c-address. Use i2c-scanner to search the correct one...
  79. #define BACKLIGHT_PIN  3
  80. #define En_pin  2
  81. #define Rw_pin  1
  82. #define Rs_pin  0
  83. #define D4_pin  4
  84. #define D5_pin  5
  85. #define D6_pin  6
  86. #define D7_pin  7
  87.  
  88. //display
  89. LiquidCrystal_I2C  lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);
  90. //*******************************************************************
  91. //LOCALIZATION - modify these according to your preferences
  92. bool weight_oz = false; //if for some strange reason ounces are preferred instead of grams
  93.  
  94. //splash text, shown after startup, eg "My own CG-scale"
  95. String t0 = "CG-SCALE   PAD73";
  96.  
  97. //battery status, eg "Battery:"
  98. String t1 = "Baterie:";
  99.  
  100. //What is hown during the initialization phase, eg. "Initializing.."
  101. String t2 = "Inicializace   ";
  102.  
  103. //Label for weight, eg "Weight:"
  104. String t3 = "Hmotnost:";
  105.  
  106. //Label for CG result, eg "CG:"
  107. String t4 = "CG:";
  108.  
  109. //Label for calibr. question, eg "Calibrate?";
  110. String t5 = "Kalibrace?";
  111.  
  112. //Label for calibr.question row2, eg "Btn 1 = Yes"
  113. String t6 = "Tl. 1 = Ano";
  114. //********************************************************************
  115. //other parameters
  116. byte n_avg = 8; //number of measurements from which the average is calculated (by the HX711 library)
  117. unsigned int lcd_min_update_interval_ms = 500; //to acoid flickering etc
  118.  
  119. //if sensor wires are crossed and scale shows negative values...
  120. bool invert_front_sensor = false; // když namontujete senzor obráceně stačí změnit na "true"
  121. bool invert_rear_sensor  = false; // když namontujete senzor obráceně stačí změnit na "true"
  122.  
  123. //calibration factors, modify these to adjust the output with known calibration weight...
  124. float sens_cal_1 = 450000; //default value, this is overwritten with value stored by calibration function
  125. float sens_cal_2 = 450000; //default value, ...
  126.  
  127. float treshold = 2; //min weight, below this "0.0" is shown
  128. float U_batt_low = 7; //low battery level, below this some info is shown
  129.  
  130. bool battAlarmShown = false; //flag to control batt info display usage (is shown only once per session)
  131.  
  132. //a counter to count.. something
  133. byte counter = 0; //counter
  134.  
  135. //EEPROM addresses
  136. unsigned int s1Addr=0;
  137. unsigned int s2Addr=4;
  138.  
  139. //button/mode things
  140. byte mode = 0; //0=norm 1,2,3... = calibration
  141. byte btn = 0;
  142. bool b1down,b2down,b3down; //button states
  143. float adjStep = 10; //mode-dependent step
  144. String adjSensor = "Predni";
  145.  
  146. //scale sensor states, flags to avoid excess commands (and time consumption)
  147. bool frontOn = false;
  148. bool rearOn = false;
  149.  
  150. //lcd update timer (to prevent updating too often, f=0.5 Hz)
  151. unsigned int t_prev_lcd_update = 0;
  152. String prevL1, prevL2; //previous lines printed
  153. byte prevLine;
  154.  
  155. //no activity --> turn off the LCD backlight and turn on the led?
  156. byte n_zero_meas = 0;
  157. byte n_sleep = 3000; //after this time without any load on sensors LCD is turned off, 3000 = 3min
  158. unsigned int t_signal = 0; //millis() since las signaling
  159. bool powerSave = false;
  160.  
  161. //-----------------------------------------------------------------
  162. //setup
  163. void setup()
  164.   {
  165.   //button pin modes
  166.   pinMode(button1Pin,INPUT_PULLUP); //button1 func
  167.   pinMode(button2Pin,INPUT_PULLUP); //button2 +
  168.   pinMode(button3Pin,INPUT_PULLUP); //button3 +
  169.  
  170.   //call the .begin function for HX711
  171.   front_scale.begin(A2, A3);
  172.   rear_scale.begin(A0, A1);
  173.  
  174.   //led or beeper
  175.   pinMode(beeperPin,OUTPUT);
  176.  
  177.   //change reference voltage to 1.1 v (internal)
  178.   //analogReference(INTERNAL); // referenční napětí se použije pokud by se Arduino napájelo přímo z baterie bez stepdown 5V
  179.  
  180.   lcd.begin (16,2); //  chars, lines
  181.   lcd.setBacklightPin(3,POSITIVE);
  182.   lcd.home ();
  183.  
  184.   Serial.begin(9600); //for serial transmission
  185.  
  186.   //splash
  187.   print2lcd(1,t0);
  188.  
  189.   lcd.setBacklight(HIGH);
  190.   print2lcd(2,t2);
  191.  
  192.   //HX711 init
  193.   //read calibration factors from EEPROM
  194.   readFromEEPROM();
  195.  
  196.   front_scale.set_scale(sens_cal_1);
  197.   front_scale.tare();
  198.   rear_scale.set_scale(sens_cal_2);
  199.   rear_scale.tare();
  200.  
  201.   //power up the sensors
  202.   front_scale.power_up();
  203.   frontOn = true;
  204.   rear_scale.power_up();
  205.   rearOn = true;
  206.  
  207.   //sw operation mode
  208.   mode = 0; //normal mode
  209.  
  210.   //button index, 0=no buttons pressed
  211.   btn = 0;
  212.   }
  213. //-----------------------------------------------------------------
  214. //main operation
  215. void loop()
  216.   {
  217.   //buttons?
  218.   areButtonsPressed();
  219.  
  220.   if (mode == 0) //normal operation
  221.     {
  222.     //read sensor values
  223.     readSensors();
  224.  
  225.     //send sensor and battery values to serial port [front rear U_batt]
  226.     Serial.println(String(eW,2)+" "+String(tW,2)+" "+String(readBattVoltage(),2));
  227.  
  228.     //countdown during init
  229.     if (counter < 6 && counter > 0) print2lcd(2,t2+String(5-counter));
  230.    
  231.     //set sensors to zero (tare)
  232.     if (counter == 5)
  233.       {
  234.       front_scale.tare();
  235.       rear_scale.tare();
  236.       giveSignal(2); //rdy
  237.       }
  238.    
  239.     //update the counter
  240.     counter++;
  241.    
  242.     //operation after init phase
  243.     if (counter > 5)
  244.       {
  245.       //total weight
  246.       float mass = eW + tW;
  247.       if (mass < treshold) mass = 0; //less than treshold = 0 g
  248.      
  249.       //calculate CG:
  250.       float cog = calculateCG();
  251.      
  252.       //check activity --> powerSave
  253.       if (mass == 0)
  254.         {
  255.         n_zero_meas = n_zero_meas + 1;  
  256.         if (n_zero_meas >= n_sleep && powerSave == false)
  257.           {
  258.           lcd.setBacklight(LOW);
  259.           powerSave = true;
  260.           t_signal = (unsigned int)millis(); //reset timer
  261.           }
  262.         }
  263.         else //some weight on scale
  264.         {
  265.         n_zero_meas = 0;  
  266.         lcd.setBacklight(HIGH);
  267.         powerSave = false;
  268.         }
  269.      
  270.       //print total mass to lcd
  271.       if (weight_oz == false) print2lcd(1,t3 +" "+String(mass,1)+" g");
  272.       if (weight_oz == true)  print2lcd(1,t3 +" "+String(mass,2)+" oz");
  273.  
  274.       //print CG if feasible
  275.       if (String(cog,1) != "0.0")
  276.         {
  277.         print2lcd(2,t4 +" "+String(cog,1)+" mm");  
  278.         }
  279.         else
  280.         {
  281.         print2lcd(2,t1+" "+String(readBattVoltage(),1)+"V");  
  282.         }
  283.  
  284.       //counter rotation...
  285.       if (counter > 20) //run between 10 and 20
  286.         {
  287.         counter = 10; //run between 10 and 20
  288.        
  289.         //voltage measurement
  290.         if (readBattVoltage() < U_batt_low && battAlarmShown == false) //if lover than value set...
  291.           {
  292.           //...show info about battery voltage
  293.           print2lcd(1,t1 +" < "+String(U_batt_low)+"V");
  294.           giveSignal(3);
  295.          
  296.           delay(2000);
  297.           battAlarmShown = true; //flag up
  298.           t_signal = (unsigned int)millis(); //reset timer
  299.           }
  300.          
  301.         //low battery, signal is given every 30 seconds
  302.         if (readBattVoltage() < U_batt_low && battAlarmShown == true)
  303.           {
  304.           if ((unsigned int)millis() - t_signal > 30000)  
  305.             {
  306.             giveSignal(3);
  307.             t_signal = (unsigned int) millis(); //update timestamp
  308.             }
  309.           }
  310.         }
  311.       }
  312.     } //end of mode 0
  313.  
  314.   //signal during power save state
  315.   if (powerSave == true && ((unsigned int)millis() - t_signal) > 20000)
  316.     {
  317.     giveSignal(3);
  318.     t_signal = (unsigned int)millis();  
  319.     }
  320.  
  321.   //other modes
  322.   //Question "calibrate?"
  323.   if (mode == 1)
  324.     {
  325.     print2lcd(1,t5);
  326.     print2lcd(2,t6);
  327.     delay(500);
  328.     }
  329.  
  330.   //cbr modes 1-7
  331.   if (mode > 1 && mode < 8)
  332.     {
  333.     print2lcd(1,adjSensor+" krok="+String(adjStep,2));
  334.    
  335.     if (mode > 1 && mode < 5) //front sensor
  336.       {
  337.       if (frontOn == false) {front_scale.power_up();  frontOn = true;}
  338.       if (rearOn == true)   {rear_scale.power_down(); rearOn = false;}
  339.      
  340.       front_scale.set_scale(sens_cal_1); //update scaling factor  
  341.       eW = front_scale.get_units(4)*1; //-1 to invert if needed //n_avg
  342.       if (weight_oz == true) eW = eW * 0.035274;
  343.      
  344.       //print factor and result to lcd
  345.       if (weight_oz == false) print2lcd(2,String(sens_cal_1,2)+" "+String(eW,2)+" g");
  346.       if (weight_oz == true)  print2lcd(2,String(sens_cal_1,2)+" "+String(eW,2)+" oz");
  347.       }
  348.       else //rear sensor
  349.       {
  350.       if (frontOn == true)  {front_scale.power_down();  frontOn = false;}
  351.       if (rearOn == false)  {rear_scale.power_up();     rearOn = true;}
  352.      
  353.       rear_scale.set_scale(sens_cal_2); //update scaling factor  
  354.       tW = rear_scale.get_units(4)*1; //-1 to invert if needed
  355.       if (weight_oz == true) tW = tW * 0.035274;
  356.      
  357.       //print factor and result to lcd
  358.       if (weight_oz == false) print2lcd(2,String(sens_cal_2,2)+" "+String(tW,2)+" g");  
  359.       if (weight_oz == true)  print2lcd(2,String(sens_cal_2,2)+" "+String(tW,2)+" oz");  
  360.       }
  361.     }
  362.  
  363.   if (mode == 8) //save values to eeprom
  364.     {              
  365.     print2lcd(1,"Ulozeni hodnot.");
  366.     print2lcd(2,"");
  367.    
  368.     saveToEEPROM();
  369.        
  370.     delay(1000);
  371.  
  372.     if (frontOn == false)  {front_scale.power_up();  frontOn = true;}
  373.     if (rearOn == false)   {rear_scale.power_up();   rearOn = true;}
  374.      
  375.     mode = 0; // back to business
  376.     }
  377.  
  378.   //power save state --> wait 5 sec after each loop
  379.   if (counter > 5 && powerSave == true && mode == 0) delay(5000);
  380.   } //end of loop
  381.  
  382. //-----------------------------------------------------------------
  383. //Functions
  384. //printing to LCD
  385. void print2lcd(int line_1_2,String text)
  386.   {
  387.   //to avoid updating too often
  388.   bool update = false;
  389.   if (line_1_2 != prevLine) update = true;
  390.   if (line_1_2 == prevLine && ((unsigned int)millis()-t_prev_lcd_update) > lcd_min_update_interval_ms) update = true;
  391.  
  392.   //check if the contents is changed.. if not --> do not update LCD
  393.   if (line_1_2 == 1 && text == prevL1) update = false;
  394.   if (line_1_2 == 2 && text == prevL2) update = false;
  395.  
  396.  
  397.   if (update == true)
  398.     {
  399.     //clear line
  400.     lcd.setCursor(0,line_1_2-1);
  401.     lcd.print("                "); //..stupid way
  402.  
  403.     //print mess
  404.     lcd.setCursor(0,line_1_2-1);
  405.     lcd.print(text);  
  406.  
  407.     //update timestanp, line number and such
  408.     prevLine = line_1_2;
  409.     t_prev_lcd_update = (unsigned int)millis();
  410.  
  411.     if (line_1_2 == 1) prevL1 = text;
  412.     if (line_1_2 == 2) prevL2 = text;
  413.     }
  414.   }
  415.  
  416. //reading the sensor values
  417. //values are put to global variables eW,tW
  418. void readSensors()
  419.   {
  420.   //front
  421.   eW = front_scale.get_units(n_avg);
  422.   if (invert_front_sensor == true) eW = eW * -1;
  423.  
  424.   if (eW < 0) eW = 0; //no negative values
  425.   if (weight_oz == true) eW = eW * 0.035274; //oz?
  426.  
  427.   //rear
  428.   tW = rear_scale.get_units(n_avg);
  429.   if (invert_rear_sensor == true) tW = tW * -1;
  430.  
  431.   if (tW < 0) tW = 0; //no negative values
  432.   if (weight_oz == true) tW = tW * 0.035274; //oz?
  433.   }
  434.  
  435. //calculate CG
  436. float calculateCG()  
  437.   {
  438.   if (eW > treshold && tW > treshold) //proceed only if there are relevant values
  439.     {
  440.     CGratio = tW / (eW + tW);
  441.     return (((WingPegDist) * CGratio)) - ((WingPegDist) / 2) + CGoffset;
  442.     }
  443.     else
  444.     {
  445.     return 0;
  446.     }
  447.   }
  448.  
  449. //read battery voltage
  450. float readBattVoltage()
  451.   {
  452.   int val = analogRead(batRefPin);
  453.   //return val*1.1/1023/0.818*9; //multiplier 0.818 is pre-calculated from resistor values and ref voltage ... použít při přímém napájení Arduina spolu s definováním referenčního napětí
  454.   return val*1.1/19.8; // koeficient pro přepočet napětí (poslední číslo)
  455.   }
  456.  
  457. //handle buttons
  458. void areButtonsPressed()
  459.   {  
  460.   btn = 0; //button index, 0 = no buttons pressed
  461.   //detect button up
  462.   //b1
  463.   if (b1down == false && digitalRead(2) == 0){b1down = true;}
  464.   else{if (b1down == true && digitalRead(2) == 1){b1down = false;btn = 1;}}
  465.    
  466.   //b2
  467.   if (b2down == false && digitalRead(3) == 0){b2down = true;}
  468.   else{if (b2down == true && digitalRead(3) == 1){b2down = false;btn = 2;}}
  469.  
  470.    //b3
  471.   if (b3down == false && digitalRead(4) == 0){b3down = true;}
  472.   else{if (b3down == true && digitalRead(4) == 1){b3down = false;btn = 3;}}
  473.  
  474.   //-------------------------------------------
  475.   //mode-spesific operations
  476.   //from basic operation to calibration mode, question?
  477.   if (mode == 0 && btn == 1 && counter > 10)
  478.     {
  479.     mode = 1; //question
  480.     btn = 0;
  481.     }
  482.  
  483.   //btn2 or 3 -> cancel, back to mode 0
  484.   if (mode == 1 && btn > 1)
  485.     {
  486.     mode = 0;  
  487.     btn = 0;
  488.     }
  489.  
  490.   //btn1 = yes --> change to calibration mode a
  491.   if (mode == 1 && btn == 1)
  492.     {
  493.     mode = 2;  
  494.     btn = 0;
  495.     }
  496.  
  497.   //front, step = 10
  498.   if (mode == 2)
  499.     {
  500.     if (btn == 1) mode++; //next
  501.     adjSensor = "Predni";
  502.     adjStep = 10;
  503.     if (btn == 2) sens_cal_1 = sens_cal_1-adjStep;
  504.     if (btn == 3) sens_cal_1 = sens_cal_1+adjStep;
  505.     btn = 0;  
  506.     }
  507.   //front, step = 1
  508.   if (mode == 3)
  509.     {
  510.     if (btn == 1) mode++; //next
  511.     adjSensor = "Predni";
  512.     adjStep = 1;
  513.     if (btn == 2) sens_cal_1 = sens_cal_1-adjStep;
  514.     if (btn == 3) sens_cal_1 = sens_cal_1+adjStep;
  515.     btn = 0;  
  516.     }
  517.   //front, step = 0.05
  518.   if (mode == 4)
  519.     {
  520.     if (btn == 1) mode++; //next
  521.     adjSensor = "Predni";
  522.     adjStep = 0.05;
  523.     if (btn == 2) sens_cal_1 = sens_cal_1-adjStep;
  524.     if (btn == 3) sens_cal_1 = sens_cal_1+adjStep;
  525.     btn = 0;  
  526.     }
  527.  
  528.   //rear, step = 10
  529.   if (mode == 5)
  530.     {
  531.     if (btn == 1) mode++; //next
  532.     adjSensor = "Zadni";
  533.     adjStep = 10;
  534.     if (btn == 2) sens_cal_2 = sens_cal_2-adjStep;
  535.     if (btn == 3) sens_cal_2 = sens_cal_2+adjStep;
  536.     btn = 0;  
  537.     }
  538.   //rear, step = 1
  539.   if (mode == 6)
  540.     {
  541.     if (btn == 1) mode++; //next
  542.     adjSensor = "Zadni";
  543.     adjStep = 1;
  544.     if (btn == 2) sens_cal_2 = sens_cal_2-adjStep;
  545.     if (btn == 3) sens_cal_2 = sens_cal_2+adjStep;
  546.     btn = 0;  
  547.     }
  548.   //rear, step = 0.05
  549.   if (mode == 7)
  550.     {
  551.     if (btn == 1) mode++; //next
  552.     adjSensor = "Zadni";
  553.     adjStep = 0.05;
  554.     if (btn == 2) sens_cal_2 = sens_cal_2-adjStep;
  555.     if (btn == 3) sens_cal_2 = sens_cal_2+adjStep;
  556.     btn = 0;  
  557.     }  
  558.   }
  559.  
  560. //EEPROM
  561. //read calibration factors
  562. void readFromEEPROM()
  563.   {
  564.   //reset EEPROM if button1 = down
  565.   if (digitalRead(2) == 0)
  566.     {
  567.     for (int i = 0;i<EEPROM.length();i++) EEPROM.write(i, 0xff);  
  568.     print2lcd(1,"EEPROM Reset");
  569.  
  570.     sens_cal_1 = 900;
  571.     sens_cal_2 = 900;
  572.  
  573.     EEPROM.put(s1Addr,sens_cal_1);
  574.     EEPROM.put(s2Addr,sens_cal_1);
  575.    
  576.     delay(1000);
  577.     }
  578.     else //read values from eeprom
  579.     {
  580.     EEPROM.get(s1Addr,sens_cal_1);
  581.     EEPROM.get(s2Addr,sens_cal_2);  
  582.     }
  583.    
  584.   Serial.println(sens_cal_1,HEX);
  585.   Serial.println(sens_cal_2,HEX);
  586.   }
  587.  
  588. //save calibration factors
  589. void saveToEEPROM()
  590.   {
  591.   EEPROM.put(s1Addr,sens_cal_1);
  592.   EEPROM.put(s2Addr,sens_cal_2);
  593.   }
  594.  
  595. //signaling with led or beeper
  596. void giveSignal(byte n)
  597.   {
  598.   byte t = 100;
  599.   for (int i=0;i<n;i++)
  600.     {
  601.     digitalWrite(beeperPin,HIGH);
  602.     delay(t);
  603.     digitalWrite(beeperPin,LOW);
  604.     delay(t);  
  605.     }
  606.   }