It's taken waaaaay too long, I had to learn to 3D print, but first had to learn to use Autodesk Fusion (at least the first 6 lessons) and as always the mechanical parts proved to be my undoing.
I will confess the first version had 2 buttons and a rotary encoder (those are really fun to write code for). I was going to allow push-button access to re-calibration and taring (tareing?). I had a lot of trouble with the formulas, there was something I was not getting so I gave up. It turns out recalibration is very rare for this accuracy. I just added a mandatory Tare at startup because I discovered the one-and-done approach had a lot of inaccuracy. I think it had to do with temperature.
The USB connector is in the back and is connected to a wall wart.
I used it for the first time this morning and the wastage of ground coffee was waaaaaay less.
/* * ToDo * */ #ifndef COFFEE_SCALE_H #define COFFEE_SCALE_H #include "CoffeeScale.h" #endif String lcdLine1; // max length 16 String lcdLine2; // max length 16 void setup() { unsigned long oldMillis = 0; Serial.begin(115200); // Remember to set Serial monitor to 115200 as well while (!Serial && (millis() < 5000)); // Wait up to 5 secs for the Serial device to be ready setupLCD(); if (0 == lcdLine1.reserve(16)) { displayLCD(0, 0, "Reserve error 1"); while (1); } if (0 == lcdLine2.reserve(16)) { displayLCD(0, 0, "Reserve error 2"); while (1); } setupScale(); lcdDisplay.clear(); displayLCD_Centered(0, "Place Bowl"); displayLCD_Centered(1, "On Scale"); oldMillis = millis(); while ((millis() - oldMillis) < TwoSecs); // wait 2 secs lcdDisplay.clear(); displayLCD_Centered(0, "TARE"); displayLCD_Centered(1, "WAIT"); coffeeScale.tare(10); oldMillis = millis(); while ((millis() - oldMillis) < TwoSecs); // wait 2 secs lcdDisplay.clear(); displayLCD_Centered(0, "Done"); displayLCD_Centered(1, "Proceed"); } void loop() { const float convertGmsToOzs = 0.035273962; float itemWeightGm = 0.0; float itemWeightOz = 0.0; float itemWeightGmRounded = 0.0; float itemWeightGmRoundedSaved = 0.0; itemWeightGm = coffeeScale.get_units(scaleSamples); itemWeightOz = itemWeightGm * convertGmsToOzs; itemWeightGmRounded = itemWeightGm + 0.5; // Only display if value has changed to try and eliminate flicker if (itemWeightGmRoundedSaved != itemWeightGmRounded) { lcdDisplay.clear(); lcdLine1 = " "; lcdLine2 = " "; lcdLine1 = String((itemWeightGm + 0.05), 1) + " Gm"; lcdLine2 = String((itemWeightOz) + 0.005, 2) + " Oz"; displayLCD_Centered(0, lcdLine1); displayLCD_Centered(1, lcdLine2); itemWeightGmRoundedSaved = itemWeightGmRounded; } }
#ifndef COFFEE_SCALE_H #define COFFEE_SCALE_H #include "CoffeeScale.h" #endif /* * ToDo * */ #include <Arduino.h> #include <Wire.h> #include <HX711.h> #include <EEPROM.h> #include <LiquidCrystal_I2C.h> LiquidCrystal_I2C lcdDisplay(0x27, 16, 2); HX711 coffeeScale; // Forward declares (probably a better name but I can't be bothered to find out void (*resetFunc)(void) = 0; void displayLCD_Centered(int Row, String textToPrint); long myOffset = 0; float myScale = 0.0; const uint8_t scaleSamples = 19; const uint8_t dataPin = 4; const uint8_t clockPin = 5; const bool FastProcessor = true; const uint32_t msecsToSecs = 1000; const uint32_t TwoSecs = 2 * msecsToSecs; struct eepromData { long Offset = 0; float Scale = 0; }; void setupEEPROM(){ int eeAddress = 0; eepromData savedData; EEPROM.get(eeAddress, savedData); myOffset = savedData.Offset; myScale = savedData.Scale; } void setupScale() { coffeeScale.begin(dataPin, clockPin, FastProcessor); coffeeScale.power_up(); // Wait up to 3 secs, retry every 1/2 sec if (!coffeeScale.wait_ready_timeout((uint32_t)3000, (uint32_t)500)) { lcdDisplay.clear(); // timed out, NOT ready displayLCD_Centered(0, "Check Scale"); displayLCD_Centered(1, "Press Button 1x"); //RCA Add a Buzzer? resetFunc(); } setupEEPROM(); coffeeScale.set_offset(myOffset); coffeeScale.set_scale(myScale); coffeeScale.set_medavg_mode(); } void setupLCD() { lcdDisplay.init(); lcdDisplay.backlight(); lcdDisplay.clear(); displayLCD_Centered(0, "Coffee Scale Rdy"); // Beep delay(TwoSecs); } void displayLCD(int Col, int Row, String textToPrint) { lcdDisplay.setCursor(Col, Row); lcdDisplay.print(textToPrint); } void displayLCD_Centered(int Row, String textToPrint) { const uint8_t Width = 17; uint8_t centeredCol = 0; uint8_t len = 0; len = textToPrint.length(); centeredCol = (((Width - len) / 2) - 1); displayLCD(centeredCol, Row, textToPrint); }
First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.
@tfmccarthy it's the 2nd file.
First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.
@tfmccarthy That version of the code still doesn't work right. To 'fix' it, in setup after setupScale(); remove all the lines to the end of setup.
The concept here is a dedicated task concept. The key variables are computed for the empty bowl on the scale and stored in EEPROM. There is NO tare function.
The Reason is the big button I chose for a tare function is very flaky. I destroyed one when soldering AFTER I had hot glued it in place. The second button sometimes 'clicks' sometimes does not. I found an example of doing a scale with the tare already done BUT of course I keep trying to make it work.
WHY, because these devices are very random, I think it's temperature fluctuations but I am not sure.
There are some other functions of unknown purpose that may affect the randomness, but I need to move on.
I am back into astronomy again, and in fact I have an organizing meeting in an hour plus I am spending more time on the big forum these days.
I figure if I keep busy enough, my body will forget to quit.
You may be asking why I don't use a different button, and the answer is I am STUBBORN, plus it's HUGE so will leave a big hole. Time will tell what I end up doing.
I also have a first cut at a sleeping version, but it fails badly and the doc'n is sparse plus I am too busy now.
Oh yes, there is another sketch needed to calculate and save the variables to EEPROM. Or you can hard code them. There was no way in hades I was going to hard code them when I have an EEPROM on the NANO. (clone old bootloader CHEAP)
BTW, the PICO-2 is on it's way!!!
Here is screen shot of what to remove
First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.
Ah! What threw me was the header guard in both files
#ifndef COFFEE_SCALE_H #define COFFEE_SCALE_H #include "CoffeeScale.h" #endif
Made me think the second file was a cpp file.
The header guard in CoffeeScale.h won't work as is. You need to move the #endif to the end of the file
EDIT: ... and drop the include "CoffeeScale.h"
The one who has the most fun, wins!
I figure if I keep busy enough, my body will forget to quit.
Newton's First Law
You may be asking why I don't use a different button, and the answer is I am STUBBORN,
I'd say, "determined." Devices should work correctly and do our bidding. Asimov's second law.
After I gander at this, I may have a few ideas. But I'm mainly interested in what gets your blood pounding.
BTW, I'm immensely impressed you learned to 3D print. I'm astonished!
The one who has the most fun, wins!
@tfmccarthy It's my first time using the guard thing, normally I wouldn't do anything as it isn't really needed in this case. RIGHT?
First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.
It's my first time using the guard thing, normally I wouldn't do anything as it isn't really needed in this case. RIGHT?
In this case, you're correct, it's not necessary.
In general, it's a healthy habit to get into as it saves headaches down the road and is trivial to add. The pattern is: all your header files (*.h) should include a header guard
#ifndef COFFEE_SCALE_H #define COFFEE_SCALE_H // actal header code here .../ #endif // COFFEE_SCALE_H
I add the comment on the #endif to help keep track of them (technically, I think there is a complaint about it, but it's common)
The idea is that a header guard allows you to include a header in multiple files, wherever you choose without causing multiple definition errors, et al.
Almost every compiler now has a compiler pragma that performs this:
#pragma once
This has the same effect as the #if - #endif pair and the compiler will do the work. (It's always a good idea to have the compiler on your side, working for you,)
I use a "belt-and-suspenders" approach
#pragma once #ifndef COFFEE_SCALE_H #define COFFEE_SCALE_H // .... add header declartions and definitions ... #endif
Thus, if the compiler fails me, I handle it. And it costs nothing.
The one who has the most fun, wins!
@tfmccarthy I like belt and suspenders, I will modify as per your instructions. I have done a tweak to the code to once again get the tare done each time without any buttons. When I post that I will also add the guards.
Thanks for educating this old dog.
First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.
@tfmccarthy The compiler did NOT complain even at level ALL
#endif // COFFEE_SCALE_HI add the comment on the #endif to help keep track of them (technically, I think there is a complaint about it, but it's common)
First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.
The compiler did NOT complain even at level ALL
By "technically" I mean according to the C++ language specification. The #endif should not have anything after it. Nothing, zero, nada. Well, maybe spaces but nothing else.
However, it is so common that practically every compiler doesn't implement that part of the language specification with a warning or error.
The one who has the most fun, wins!
Updated code with include guards done right, and the startup now working as intended after going down a rabbit hole.
Main ino
/* * ToDo * */ #include "CoffeeScale.h" String lcdLine1; // max length 16 String lcdLine2; // max length 16 void setup() { unsigned long oldMillis = 0; Serial.begin(115200); // Remember to set Serial monitor to 115200 as well while (!Serial && (millis() < 5000)); // Wait up to 5 secs for the Serial device to be ready setupLCD(); if (0 == lcdLine1.reserve(16)) { displayLCD(0, 0, "Reserve error 1"); while (1); } if (0 == lcdLine2.reserve(16)) { displayLCD(0, 0, "Reserve error 2"); while (1); } setupScale(); lcdDisplay.clear(); displayLCD_Centered(0, "Place Empty Bowl"); displayLCD_Centered(1, "On Scale"); oldMillis = millis(); while ((millis() - oldMillis) < ThreeSecs); // wait 3 secs coffeeScale.tare(10); } void loop() { const float convertGmsToOzs = 0.035273962; float itemWeightGm = 0.0; float itemWeightOz = 0.0; float itemWeightGmRounded = 0.0; float itemWeightGmRoundedSaved = 0.0; itemWeightGm = coffeeScale.get_units(scaleSamples); itemWeightOz = itemWeightGm * convertGmsToOzs; itemWeightGmRounded = itemWeightGm + 0.5; // Only display if value has changed to try and eliminate flicker if (itemWeightGmRoundedSaved != itemWeightGmRounded) { lcdDisplay.clear(); lcdLine1 = " "; lcdLine2 = " "; lcdLine1 = String((itemWeightGm + 0.05), 1) + " Gm"; lcdLine2 = String((itemWeightOz) + 0.005, 2) + " Oz"; displayLCD_Centered(0, lcdLine1); displayLCD_Centered(1, lcdLine2); itemWeightGmRoundedSaved = itemWeightGmRounded; } }
.h file
#pragma once #ifndef COFFEE_SCALE_H #define COFFEE_SCALE_H /* * ToDo * */ #include <Arduino.h> #include <Wire.h> #include <HX711.h> #include <EEPROM.h> #include <LiquidCrystal_I2C.h> LiquidCrystal_I2C lcdDisplay(0x27, 16, 2); HX711 coffeeScale; // Forward declares void (*resetFunc)(void) = 0; void displayLCD_Centered(int Row, String textToPrint); long myOffset = 0; float myScale = 0.0; const uint8_t scaleSamples = 19; const uint8_t dataPin = 4; const uint8_t clockPin = 5; const bool FastProcessor = true; const uint32_t msecsToSecs = 1000; const uint32_t OneSec = 1 * msecsToSecs; const uint32_t ThreeSecs = 3 * msecsToSecs; struct eepromData { long Offset = 0; float Scale = 0; }; void setupEEPROM(){ int eeAddress = 0; eepromData savedData; EEPROM.get(eeAddress, savedData); myOffset = savedData.Offset; myScale = savedData.Scale; } void setupScale() { coffeeScale.begin(dataPin, clockPin, FastProcessor); coffeeScale.power_up(); // Wait up to 3 secs, retry every 1/2 sec if (!coffeeScale.wait_ready_timeout((uint32_t)3000, (uint32_t)500)) { lcdDisplay.clear(); // timed out, NOT ready displayLCD_Centered(0, "Check Scale"); displayLCD_Centered(1, "Press Button 1x"); resetFunc(); } setupEEPROM(); coffeeScale.set_offset(myOffset); coffeeScale.set_scale(myScale); coffeeScale.set_medavg_mode(); } void setupLCD() { lcdDisplay.init(); lcdDisplay.backlight(); lcdDisplay.clear(); displayLCD_Centered(0, "Coffee Scale Rdy"); delay(OneSec); } void displayLCD(int Col, int Row, String textToPrint) { lcdDisplay.setCursor(Col, Row); lcdDisplay.print(textToPrint); } void displayLCD_Centered(int Row, String textToPrint) { const uint8_t Width = 17; uint8_t centeredCol = 0; uint8_t len = 0; len = textToPrint.length(); centeredCol = (((Width - len) / 2) - 1); displayLCD(centeredCol, Row, textToPrint); } #endif // COFFEE_SCALE_H
First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.
The scale is working as envisioned including the initial TARE. It turns out the bowl can be placed on any time before the first measurement cycle.
NEXT is adding auto power down after 5 minutes and the button will wake it up. I have the most technical sleep information I could find and will now massage it into a form usable by the scale code. It will be tested standalone first then shoehorned into the scale sketch. I hope it won't take more than a couple of weeks but I rarely can work more than a couple hours a day and all to many days it's zero.
BTW, the PICO-2 showed up today. so I might be down some rabbit holes as well.
-30-
First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.
I modified the code to enable a sleep forever after 5 mins, and a push button to wake it up. I also made some minor changes to timing and messaging plus removed all the empty lines just because.
If anyone wants to see the 'final' version let me know and I will post it.
First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.