///////////////////////////////////////////////////////////////////////////////
// Скетч весы с автоматическим определением человека (ВСАОЧ) на Arduino Nano //
///////////////////////////////////////////////////////////////////////////////
// Состав материалов:
// - Arduino Mega
// - Дисплей 128X64 (можно заменить на любой другой поддерживаемый библиотекой U8g2lib.h)
// - Кнопки, 1шт
// - Переключатель
// - Аккумулятор 18532
// Подключение контроллера:
// -
// Инициализация библиотек
#include <Arduino.h>
#include <U8g2lib.h>
#include <GyverHX711.h>
#include <EEPROM.h>
#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif
#define SEL1 2 // серая
#define ENT1 3 // черная
#define hold 1000 // время удержания кнопки
#define HX711_SLK A0
#define HX711_DT A1
//U8G2_ST7565_ZOLEN_128X64_F_4W_SW_SPI lcd(U8G2_R0, /* clock=*/ 13, /* data=*/ 11, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8); //для дисплея V2.2 раскоментировать Flip в библиотеке u8x8_d_st7565.с
U8G2_SSD1306_128X64_NONAME_F_SW_I2C lcd(U8G2_R0, /* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE); // All Boards without Reset of the Display
GyverHX711 sensor(HX711_DT, HX711_SLK, HX_GAIN64_A); // инициализация весового сенсора
// HX_GAIN128_A - канал А усиление 128
// HX_GAIN32_B - канал B усиление 32
// HX_GAIN64_A - канал А усиление 64
////////////////////////////////////////////////////////////////////////////////////
////////////////////////// обьявления переменных ///////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
int page = 0; // страница меню
int select0 = 0; // выбор 0
int select1 = 0; // выбор 1
float ves_izm = 0; // вес измереный
float ves_disp = 0; // вес отображаемый на дисплее
float ves_izm_new = 0; // вес измереный новый
float ves_izm10[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // последние 10 измерений
float ves_mid[6] = {0, 0, 0, 0, 0, 0}; // среднее значение весов
String human[] = {"АВТО", "Гость", "Иван", "Гульнара", "Лёня", "Илюша"}; // пользователи
// массив для тестирования в wokwi без контроллера и первой прошивке МК
float ves1[6] = { 1, 1, 1, 1, 1, 1}; // архив для пользователя Гость
float ves2[6] = {101, 98, 103, 99, 99, 101}; // архив для пользователя Иван
float ves3[6] = {64, 64, 64, 64, 64, 64}; // архив для пользователя Гульнара
float ves4[6] = {40, 40, 40, 40, 40, 41}; // архив для пользователя Лёня
float ves5[6] = {22, 22, 22, 22, 22, 22}; // архив для пользователя Илюша
// массив куда пишуться данные из памяти
float ves1m[6] = { 0, 0, 0, 0, 0, 0};
float ves2m[6] = { 0, 0, 0, 0, 0, 0};
float ves3m[6] = { 0, 0, 0, 0, 0, 0};
float ves4m[6] = { 0, 0, 0, 0, 0, 0};
float ves5m[6] = { 0, 0, 0, 0, 0, 0};
String str0, str1, str2 = "0"; // для преобразования в строку
int nomer = 0; // номер пользователя
bool ENT, SEL = 0; // кнопки
float proc = 15; // погрешность определения пользователя % от среднего за последнии 6 измерений
float delta = 1000; // в течении delta мс на весах сбрасывает все флаги и тарирует датчик
long timer2 = millis(); // таймер остановки измерений
long timer3 = millis(); // таймер обнуления измерений
int step = 0; // шаги измерения
bool flag_start = 0; // флаг начала измерений
bool flag_end = 0; // флаг конца измерений
bool flag_zero = 0; // флаг обнуления весов
////////////////////////// настройки при старте контроллера
void setup() {
lcd.begin(); // инициализация дисплея
lcd.setContrast(50); // контраст дисплея
lcd.enableUTF8Print(); // выбор русской кодировки
Serial1.begin(9600); // открытия порта №1 для вывода отладочной информации
//pinMode(ENT1, INPUT_PULLUP); // подтяжка к питанию входов кнопок
//EEPROM.begin(128); // для ESP
// записываеться стандартная таблица весов пользователей, только один раз при первой прошивке микроконтролера
EEPROM.put(0, ves1);
EEPROM.put(24, ves2);
EEPROM.put(48, ves3);
EEPROM.put(72, ves4);
EEPROM.put(96, ves5);
//EEPROM.commit(); // для ESP
EEPROM.get(0, ves1m); // при загрузки всегда считывание из памяти данных пользователей
EEPROM.get(24, ves2m);
EEPROM.get(48, ves3m);
EEPROM.get(72, ves4m);
EEPROM.get(96, ves5m);
delay(500); // нужно выждать готовность датчика
sensor.tare(); // калибровка нуля. Устанавливает 0
attachInterrupt(0, isr, CHANGE); //прерывание для кнопки 1
attachInterrupt(1, isr2, CHANGE); //прерывание для кнопки 2
}
volatile uint32_t debounce;
void isr() {
// оставим 100 мс таймаут на гашение дребезга
if (millis() - debounce >= 500 && digitalRead(2)) {
debounce = millis();
SEL = 1; // серая
}
}
volatile uint32_t debounce2;
void isr2() {
// оставим 100 мс таймаут на гашение дребезга
if (millis() - debounce2 >= 500 && digitalRead(3)) {
debounce2 = millis();
ENT = 1; // черная
}
}
void loop() {
////////////////////////// картинки иконок сгенерированы из BMP программой Конвертор by Kinguf
static const unsigned char wave[] U8X8_PROGMEM = {
0xFF, 0x3F, 0x01, 0x20, 0x11, 0x20, 0x29, 0x20, 0x29, 0x20, 0x29, 0x20, 0x45, 0x20, 0xFD, 0x2F, 0x41, 0x28, 0x81, 0x24, 0x81, 0x24, 0x01, 0x23, 0x01, 0x20, 0xFF, 0x3F,
};
static const unsigned char arrow[] U8X8_PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x07, 0xFC, 0x0F, 0xFC, 0x1F, 0xFC, 0x1F, 0xFC, 0x0F, 0x00, 0x07, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
};
if (sensor.available()) {
ves_izm = sensor.read(); // измерение веса с датчиков
ves_izm = ves_izm * 1.0 / 210; // коэффициент вычисляемый эталонным весом, т.е. калибровка
if (ves_izm < 0) {
ves_izm = 0;
}
//Serial1.println(ves_izm);
if (millis() > timer2 + 400) { // задержка начала измерений 400мс
if ((abs(ves_izm - ves_izm_new) < 0.6) && (ves_izm > 1) && (flag_start == 0)) { // условие фиксации веса на определенном уровне с разбросом 0.6кг
flag_start = 1; // взводим флаг начала 10ти измерений
}
ves_izm_new = ves_izm;
timer2 = millis();
}
if ((flag_start) && (flag_end == 0) && (page == 0) ) {
ves_izm10[step] = ves_izm; // сохранение последних 10ти измерений
step++;
if (step == 10) { // если измерили 10 измерений
ves_izm = 0;
for (int i = 0; i < 10; i++) { // расчет среднего арифметического
ves_izm = ves_izm + ves_izm10[i];
}
ves_izm = ves_izm / 10.0;
step = 0; // обнуление шагов
flag_end = 1; // взвод флага конца измерений
EEPROM.get(0, ves1m); // считывание из памяти данных пользователей
EEPROM.get(24, ves2m);
EEPROM.get(48, ves3m);
EEPROM.get(72, ves4m);
EEPROM.get(96, ves5m);
}
}
if ((ves_izm < 1) && (flag_start)) { // обнуление весов
if (flag_zero == 0) {
flag_zero = 1;
timer3 = millis();
}
if (millis() > timer3 + delta) { // при измерении 0 в течении delta мс на весах сбрасывает все флаги и тарирует датчик
// запись последнего измеренного веса в память
switch (nomer) {
case 1:
for (int i = 5; i > 0; i--) { // сдвиг веса, удаление самого старого и запись нового
ves1m[i] = ves1m[i - 1]; // для пользователя 1
}
ves1m[0] = ves_disp;
EEPROM.put(0, ves1m);
//EEPROM.commit(); // для ESP
break;
case 2:
for (int i = 5; i > 0; i--) { // сдвиг веса, удаление самого старого и запись нового
ves2m[i] = ves2m[i - 1];
}
ves2m[0] = ves_disp;
EEPROM.put(24, ves2m);
//EEPROM.commit(); // для ESP
break;
case 3:
for (int i = 5; i > 0; i--) { // сдвиг веса, удаление самого старого и запись нового
ves3m[i] = ves3m[i - 1];
}
ves3m[0] = ves_disp;
EEPROM.put(48, ves3m);
//EEPROM.commit(); // для ESP
break;
case 4:
for (int i = 5; i > 0; i--) { // сдвиг веса, удаление самого старого и запись нового
ves4m[i] = ves4m[i - 1];
}
ves4m[0] = ves_disp;
EEPROM.put(72, ves4m);
//EEPROM.commit(); // для ESP
break;
case 5:
for (int i = 5; i > 0; i--) { // сдвиг веса, удаление самого старого и запись нового
ves5m[i] = ves5m[i - 1];
}
ves5m[0] = ves_disp;
EEPROM.put(96, ves5m);
//EEPROM.commit(); // для ESP
break;
}
flag_start = 0;
flag_end = 0;
flag_zero = 0;
nomer = 0;
sensor.tare();
}
}
for (int i = 1; i < 6; i++) { // обнуление средних весов
ves_mid[i] = 0;
}
for (int i = 0; i < 6; i++) {
ves_mid[1] = ves_mid[1] + ves1m[i]; // вычисление суммы 6 измерений для 1 пользователя
ves_mid[2] = ves_mid[2] + ves2m[i]; // вычисление суммы 6 измерений для 2 пользователя
ves_mid[3] = ves_mid[3] + ves3m[i]; // вычисление суммы 6 измерений для 3 пользователя
ves_mid[4] = ves_mid[4] + ves4m[i]; // вычисление суммы 6 измерений для 4 пользователя
ves_mid[5] = ves_mid[5] + ves5m[i]; // вычисление суммы 6 измерений для 5 пользователя
}
for (int i = 1; i < 6; i++) {
ves_mid[i] = ves_mid[i] / 6.0; // вычисление среднего для всех пользователей
}
if (flag_end == 0) {
ves_disp = ves_izm;
}
if ((nomer == 0) and (flag_end == 1)) { // если измерение закончено и включено автоматическое определение пользователя
ves_disp = ves_izm;
if (abs(ves_izm - ves_mid[2]) <= (ves_mid[2]*proc) / 100.0) { // если вес с погрешностью proc равен среднему 2 пользователя
nomer = 2;
}
else if (abs(ves_izm - ves_mid[3]) <= (ves_mid[3]*proc) / 100.0) {
nomer = 3;
}
else if (abs(ves_izm - ves_mid[4]) <= (ves_mid[4]*proc) / 100.0) {
nomer = 4;
}
else if (abs(ves_izm - ves_mid[5]) <= (ves_mid[5]*proc) / 100.0) {
nomer = 5;
}
else {
nomer = 1; // если нет в памяти такого веса то имя пользователя Гость
}
}
///////////////////////////////////////////////////////// МЕНЮ
}
switch (page) {
///////////////////// главное меню
case 0:
if (SEL == 1) {
Serial1.println("SEL-0");
SEL = 0;
if ((flag_start == 0) && (flag_end == 0) && (flag_zero == 0)) {
nomer++;
}
if (nomer > 5) {
nomer = 0;
}
}
if (ENT == 1) {
Serial1.println("ENT-0");
ENT = 0;
page = 1;
}
break;
///////////////////// меню настроек
case 1:
if (SEL == 1) {
Serial1.println("SEL-1");
SEL = 0;
select1++;
if (select1 > 2) {
select1 = 0;
}
}
if (ENT == 1) {
Serial1.println("ENT-1");
ENT = 0;
page = 0;
select1 = 0;
}
break;
}
lcd.clearBuffer(); // очистка экрана
switch (page) {
////////////////////// главное меню
case 0:
//////lcd.setFont(u8g2_font_haxrcorp4089_t_cyrillic);
{
lcd.setFont(u8g2_font_10x20_t_cyrillic);
lcd.setCursor(0, 18);
lcd.print(human[nomer]);
lcd.setFont(u8g2_font_osb35_tf);
lcd.setCursor(0, 64);
lcd.print(ves_disp, 1);
int x = 0;
if (ves_disp < 10) {
x = 28;
}
else if (ves_disp < 100) {
x = 56;
}
else x = 84;
if (flag_end == 1) {
lcd.drawXBMP(x, 31, 14, 14, wave); // вывод иконки конца измерений
}
if (nomer == 0) {
lcd.setFont(u8g2_font_10x20_t_cyrillic);
float reg0 = 100;
str0 = String(reg0, 0) + "%";
lcd.setCursor(80, 18);
lcd.print(str0);
}
if (nomer != 0) {
lcd.drawRFrame(81, 0, 47, 27, 4);
lcd.setFont(u8g2_font_t0_11b_mf);
float reg0 = 0;
float reg1 = 0;
switch (nomer) {
case 1:
reg0 = ves1m[0];
reg1 = ves1m[1];
break;
case 2:
reg0 = ves2m[0];
reg1 = ves2m[1];
break;
case 3:
reg0 = ves3m[0];
reg1 = ves3m[1];
break;
case 4:
reg0 = ves4m[0];
reg1 = ves4m[1];
break;
case 5:
reg0 = ves5m[0];
reg1 = ves5m[1];
break;
}
str1 = "1 " + String(reg0, 1);
str2 = "2 " + String(reg1, 1);
lcd.setCursor(84, 12);
lcd.print(str1);
lcd.setCursor(84, 23);
lcd.print(str2);
}
break;
}
///////////////////// меню настройки
case 1:
{
lcd.setFont(u8g2_font_10x20_t_cyrillic);
lcd.setCursor(20, 11);
lcd.print("Настройки");
lcd.setFont(u8g2_font_haxrcorp4089_t_cyrillic);
lcd.setCursor(17, 28);
lcd.print("Погрешность");
lcd.setCursor(17, 44);
lcd.print("Отключение");
lcd.setCursor(17, 60);
lcd.print("IP");
str0 = String(proc, 0) + "%";
str1 = String(delta, 0) + "мс";
str2 = "192.168.0.154";
lcd.setCursor(100, 28);
lcd.print(str0);
lcd.setCursor(80, 44);
lcd.print(str1);
lcd.setCursor(60, 60);
lcd.print(str2);
switch (select1) {
case 0: lcd.drawXBMP(0, 17, 14, 14, arrow); break;
case 1: lcd.drawXBMP(0, 33, 14, 14, arrow); break;
case 2: lcd.drawXBMP(0, 49, 14, 14, arrow); break;
}
break;
}
} //switch (page)
lcd.sendBuffer(); // отрисовка дисплея
} //loop