Hackster is hosting Hackster Holidays, Ep. 6: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Monday!Stream Hackster Holidays, Ep. 6 on Monday!
Gyula Ősi
Published © CC BY-NC

Smart Coffee Machine with Arduino and Bluetooth

Not just a Smart Coffee Maker with Smartphone control, it's a multipurpose ThermoGraph also!

BeginnerFull instructions provided1 hour16,217

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
HC-05 Bluetooth Module
HC-05 Bluetooth Module
×1
DS18B20 Temperature Sensor 1m
HARDWARIO DS18B20 Temperature Sensor 1m
×1

Software apps and online services

Serial Bluetooth Terminal created by Kai Morich
Arduino IDE
Arduino IDE

Story

Read more

Schematics

Smart Coffee Machine - Ősi Gyula

Code

Smart Coffee Maker - Ősi Gyula

Arduino
// Program Code - Smart Coffee Maker. Developed on Arduino Nano, uses ST7920 display, HC-05 Bluetooth adapter,
//                                    Dallas DS18B20 temp sensor, relay, button, leds, buzzer and a CD4017BE decade counter.
//   
// Can be controlled via Bluetooth by sending commands and working parameters or using the Button... or just load fresh water!
//
// Functions:
//   -Graph: dynamic temperature graph on the screen
//   -Autostart: loading fresh water causes temperature drop, and the system will start
//               This function can be enabled / disabled by sending "Auto", and the setting will be stored in the EEPROM memory
//   -Analysis during coffee making: 60 seconds after starting the system compares the current and initial temperature values
//                                   and draws conclusions which can be:
//                                   1. You forgot the water (temperature is too high)
//                                   2. Coffee for Two (less water - more temp elevation)
//                                   3. Coffee for Six (more water - less temp elevation)
//                                   4. Heating Coil error (no significant temp elevation from start)
//   -Sensor error detection (getting abnormal values)
//   -Celsius or Fahrenheit working: can be switched by sending C of F from the Smartphone, even during a coffee making procedure.
//                          Initial and previous temperature values will be converted used for statistics and analysis.
//                          This setting is stored in the EEPROM memory.
//   -Test Mode: attaching an external temperature sensor the system can be used as a multipurpose temperature meter.
//               In this mode the graph interval is between -20C / -4F and +128C / +262F.
//               Sending numeric values between 1 and 999999 will be accepted as measurement interval in seconds, so 
//                 the graph width can be between 128 second and 11.57 days.
//   -Bluetooth communication: accepts commands and sends reports, detects if a smartphone connects or disconnects
//   -LED bar control: the MCU controls a CD4027BE decade counter, and the LED bar will step at all temp measurement cycle
// --------------------------------------------------------------------------------------------------------------------------------
//          
// designed, built and programmed 
// by Gyula Osi
//
// All rights reserved.

// ---- Display                               
#include "U8glib.h"
//U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE);      // OLED display I2C Bus, SDA(TX) A4, SCL(RX) A5
U8GLIB_ST7920_128X64 u8g(11, 10, 9, U8G_PIN_NONE);  // display constructor PIN6-D11, PIN5-D10, PIN4-D9
byte frame = 0;                                     // start logo pointer
  
// ---- Ports & Controlled Peripherals
// red led on A0     
// grn led on A1
const int bright[2] = {0, 500};
const byte buzzer = 3;
const byte cd4017beCLK = 4;                         // control CD4017BE decade counter
const byte cd4017beRST = 5;
const byte btn = 13;
const byte relay = 12;

// ---- System Strings
#define strSize 3
String str[strSize];                                // system events and reports printout
#define reportSize 5
const String reportStr[reportSize] = {"Starting...", "Going standby soon...", "Stopped.", " ***  TEST MODE  ***", "Normal mode."};
//#define standbySize 5
//const String standbyStr[standbySize] = {"Press key", ">", "or", "send", "start"};
const String autostartStr[2] = {"Autostart off.", "Autostart on."};

// ---- Structure of System State Binary Flag Array  
#define flagElements 6
bool binFlags[flagElements] = {0, 1, 1, 0, 0, 0};
  // ---- elements [i] purpose             init/standby mode val
  //                0  checked             0
  //                1  finished            1
  //                2  standby             1
  //                3  standby msg sent    0
  //                4  coil/sensor error   0
  //                5  test mode           0

// ---- Temperature Measurement and Related Features
#include "OneWire.h" 
#include "DallasTemperature.h"
#define DS18B20 2                       // setup the OneWire bus on D2
OneWire temprWire(DS18B20);             // setup DS18B20 to work on the OneWire bus
DallasTemperature sensors(&temprWire);
float tempr;                            // measured value
float temprInit;                        // copy of measured value for one time comparison
float temprPrev;                        // copy of measured value for cyclic comparison
#define mUnit 2
float temprBottomLim[mUnit] = { 0, 26};  // autostart under tempr of AND one-time analysis under tempr of
const char unit[mUnit] = {'F', 'C'};
const String unitStr[mUnit] = {"Fahrenheit mode.", "Celsius mode."};
float trendSens[mUnit] = { 0, 0.1};  // +/- range (F/C) which will evaulated as constant temperature
                                     //   *** Fahrenheit reference values will be calculated at startup!
bool trend[2] = {1, 0};              // describes temperature trends as below
  // ---- elements [i] purpose
  //                0  changing
  //                1  trend                  
#define checkSize 7
const String checkStr[checkSize] = {"Water detected!", "Heating coil error!", "Coffee for six", "Coffee for two", "You forgot the water!", "Sensor error!", "Your Coffee is ready."};
float temprCheck[mUnit][checkSize] = {{ }, {-0.15, 5, 17, 28, 60, -127, 110}};
  //                                       |<--     REL     -->|<--CONST-->|
  //                  [j] F / C
  //               ---------------------------------------------
  // ---- elements [i] purpose                   used as
  //                0  autostart                 rel value
  //                1  coil error detection      rel value
  //                2  water for six             rel value
  //                3  water for two             rel value
  //                4  no water detection        rel value
  //                5  no signal on D2           const reference
  //                6  boiling point             const reference

// ---- Graph Declarations and Variables
#define posYsize 128
byte posY[posYsize];
byte pointer = 0;
#define refrElements 42
float temprRefr[refrElements];
#define rangeElements 2  // min / max
#define mode 2           // normal / test mode
float graphRange[mUnit][mode][rangeElements] = {{{ }, { }}, {{24, 127.938889 + 2}, {-20, graphRange[1][0][1]}}};
  //                     [k]        0        1        0        1     
  //                  [j]           |        0        |        1
  // ---- elements [i]              |        |        |        |
  //                0        minFnorm maxFnorm minFtest maxFtest
  //                1        minCnorm maxCnorm minCtest maxCtest
float graphDiff[mUnit][mode];  // vertical steps by temperature

// ---- System Timers, Control & Analytics
#include <elapsedMillis.h>  
elapsedMillis timer0;       // 8-bit, PWM timer, used by function elapsedMillis()
unsigned long tmrPrev = 0;  // the elapsed will be the previous when the interval is up
bool cyclic = 0;
const long tmrInt[5] = {500, 2000, 60000, 640000, 800000};
  // ---- elements [i] purpose             
  //                0  meas interval (test mode), led ctrl
  //                1  meas interval (normal mode)
  //                2  one time analytics (normal mode, during coffee making) / cyclic analytics (standby mode)
  //                3  sending standby msg (normal mode, after coffee making)
  //                4  system standby (normal mode, after coffee making)
long copyof_tmrInt0 = tmrInt[0];  
const String tmrInt0Str = "Interval changed."; 

// ---- Configuration of Serial Communication
const byte btState = 6;
bool conn = 0;
const String connStr[2] = {"Connection lost.", "Smartphone connected."};
#include <SoftwareSerial.h>   
const int RX1 = 7;
const int TX1 = 8;
SoftwareSerial sUART(RX1,TX1); 
#define exRXSize 6
char exRX[exRXSize];             // variable to receive data from the serial port

// ---- Memory Management
#include <EEPROM.h>
#define occBytes 2
const byte addr[occBytes] = { 0, 1};
bool sysSettings[occBytes]; 
  // ---- Memory Map addr[i] | data | descr     | stored val
  //                      -----------------------------------------------------
  //                      0  | bool | autostart | 0: no autostart, 1: autostart
  //                      1  | bool | meas unit | 0: Fahrenheit,   1: Celsius

// ---- Terminators & The Cleanup Crew
const char termCharSpc = ' ';
const String termStr;
const byte termByte = 0;
const bool termBool = 0;

const uint8_t frame0[] U8G_PROGMEM = {
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x00, 0xFE, 0xFF, 
  0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 0xFC, 0xFF, 0x7F, 0xF0, 
  0x3F, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 
  0xFC, 0xFF, 0x7F, 0xF0, 0x8F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 
  0xFF, 0xFF, 0x1F, 0xFF, 0xF1, 0xFF, 0xFF, 0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xF1, 0xFF, 0xFF, 0xFF, 
  0x8F, 0xFF, 0x1F, 0x7F, 0x8C, 0x3F, 0x1E, 0xFF, 0x00, 0xFE, 0x1F, 0xFF, 
  0xF1, 0x00, 0x18, 0xC0, 0x8F, 0xFF, 0x1F, 0x7F, 0x8C, 0x3F, 0x1E, 0xFF, 
  0x00, 0xFE, 0x1F, 0xFF, 0xF1, 0x00, 0x18, 0xC0, 0x8F, 0xFF, 0x1F, 0x1F, 
  0x0C, 0x3E, 0x1E, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0x31, 0xFE, 0x7F, 0xFC, 
  0x8F, 0xFF, 0x1F, 0x1F, 0x0C, 0x3E, 0x1E, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 
  0x31, 0xFE, 0x7F, 0xFC, 0x0F, 0x0E, 0x18, 0x1F, 0x0C, 0x3E, 0x1E, 0xFC, 
  0x00, 0xF8, 0x1F, 0x7C, 0x30, 0xFE, 0x7F, 0xF0, 0x0F, 0x0E, 0x18, 0x1F, 
  0x0C, 0x3E, 0x1E, 0xFC, 0x00, 0xF8, 0x1F, 0x7C, 0x30, 0xFE, 0x7F, 0xF0, 
  0x0F, 0xFE, 0x18, 0x1F, 0x0C, 0x3E, 0x1E, 0x3C, 0x3E, 0xF8, 0x1F, 0x7C, 
  0xF0, 0x00, 0x7E, 0xF0, 0x0F, 0xFE, 0x18, 0x1F, 0x0C, 0x3E, 0x1E, 0x3C, 
  0x3E, 0xF8, 0x1F, 0x7C, 0xF0, 0x00, 0x7E, 0xF0, 0x0F, 0xFE, 0x18, 0x1F, 
  0x0C, 0x3E, 0x1E, 0x3C, 0x3E, 0xF8, 0x1F, 0x7C, 0xF0, 0x3F, 0x78, 0xF0, 
  0x0F, 0xFE, 0x18, 0x1F, 0x0C, 0x3E, 0x1E, 0x3C, 0x3E, 0xF8, 0x1F, 0x7C, 
  0xF0, 0x3F, 0x78, 0xF0, 0x0F, 0xFE, 0x18, 0x1F, 0x0C, 0x3E, 0x1E, 0x3C, 
  0x3E, 0xF8, 0x1F, 0x7C, 0xF0, 0x3F, 0x78, 0xF0, 0x0F, 0xFE, 0x18, 0x1F, 
  0x0C, 0x3E, 0x1E, 0x3C, 0x3E, 0xF8, 0x1F, 0x7C, 0xF0, 0x3F, 0x78, 0xF0, 
  0x3F, 0x00, 0x7E, 0x00, 0x3C, 0x80, 0x07, 0xF0, 0x00, 0xF8, 0x7F, 0x00, 
  0x3C, 0x00, 0x1E, 0xC0, 0x3F, 0x00, 0x7E, 0x00, 0x3C, 0x80, 0x07, 0xF0, 
  0x00, 0xF8, 0x7F, 0x00, 0x3C, 0x00, 0x1E, 0xC0, 0xFF, 0xFF, 0xFF, 0x1F, 
  0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0x1F, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x7E, 0x06, 0xE6, 0x3F, 0x06, 0xC6, 0x7F, 0xFE, 0xE7, 0x3F, 0x7E, 
  0xFE, 0xC7, 0x7F, 0x00, 0x00, 0x30, 0x06, 0x66, 0x60, 0x06, 0x66, 0x00, 
  0x60, 0x60, 0x60, 0x30, 0x06, 0x60, 0x00, 0x00, 0x00, 0x30, 0x06, 0x66, 
  0x60, 0x06, 0x66, 0x00, 0x60, 0x60, 0x60, 0x30, 0x06, 0x60, 0x00, 0x00, 
  0x00, 0x30, 0x1E, 0x66, 0x60, 0x06, 0x66, 0x00, 0x60, 0x60, 0x60, 0x30, 
  0x06, 0x60, 0x00, 0x00, 0x00, 0x30, 0x3E, 0x66, 0x60, 0x06, 0x66, 0x00, 
  0x60, 0x60, 0x60, 0x30, 0x06, 0x60, 0x00, 0x00, 0x00, 0x3C, 0x7E, 0xE6, 
  0x61, 0x1E, 0xC7, 0x3F, 0x70, 0xE0, 0x3F, 0x3C, 0xFE, 0xC3, 0x3F, 0x00, 
  0x00, 0x3C, 0x7E, 0xE6, 0x61, 0x1E, 0xC7, 0x3F, 0x70, 0xE0, 0x3F, 0x3C, 
  0xFE, 0xC3, 0x3F, 0x00, 0x00, 0x3C, 0xDE, 0xE7, 0x61, 0x1E, 0x07, 0x70, 
  0x70, 0xE0, 0x1D, 0x3C, 0x1E, 0x00, 0x70, 0x00, 0x00, 0x3C, 0x1E, 0xE7, 
  0x61, 0x1E, 0x07, 0x70, 0x70, 0xE0, 0x31, 0x3C, 0x1E, 0x00, 0x70, 0x00, 
  0x00, 0x3C, 0x1E, 0xE6, 0x61, 0x1E, 0x07, 0x70, 0x70, 0xE0, 0x61, 0x3C, 
  0x1E, 0x00, 0x70, 0x00, 0x00, 0x3C, 0x1E, 0xE6, 0x61, 0x1E, 0x07, 0x70, 
  0x70, 0xE0, 0x61, 0x3C, 0x1E, 0x00, 0x70, 0x00, 0x00, 0x7F, 0x1E, 0xE6, 
  0x3F, 0xFC, 0xE3, 0x3F, 0x70, 0xE0, 0x61, 0x7E, 0xFE, 0xE7, 0x3F, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 
};

const uint8_t frame1[] PROGMEM = { 
  0xF8, //  11111000 00
  0x21, //  00100001 01
  0x22, //  00100010 02
  0x24, //  00100100 03
  0x08, //  00001000 04
  0x10, //  00010000 05
  0x24, //  00100100 06
  0x44, //  01000100 07
  0x0F, //  00001111 08
  0x04, //  00000100 09
  0x04, //  00000100 10 
  0x03, //  00000011 11
};

const uint8_t frame2[] PROGMEM = { 
  0x08, //  00001000 00
  0x1C, //  00011100 01
  0x3E, //  00111110 02
  0x7F, //  01111111 03
};

const uint8_t frame3[] PROGMEM = {
  0x7F, //  01111111 00
  0x3E, //  00111110 01 
  0x1C, //  00011100 02
  0x08, //  00001000 03       
};

void setup() { 
  u8g.begin(); 
  fillArrays();
  pinMode(cd4017beCLK, OUTPUT);
  pinMode(cd4017beRST, OUTPUT);
  CD4017BE(0);                  // send RESET to Decade Counter IC
  pinMode(btState, INPUT);
  pinMode(btn, INPUT); 
  pinMode(relay, OUTPUT); 
  sUART.begin(9600); 
  memo(2);                      // load system settings from EEPROM
  sensors.begin();              // start DS18B20
  temprMeas();
  temprInit = tempr;           
  graph(2);                     // init graph
  ledHandler(0);
}

void loop() { 
    sysControl();
    checkBtn();
    checkConn();
    RX();
    u8g.firstPage();     
    do {
      draw();
    } 
    while(u8g.nextPage());
      if (frame == 0) { 
        delay(3000);
        frame = 1;
        scrClr(); 
      }
}

  // ---- Memory Management
  //        call param
  //             2: read at startup
  //          0, 1: write to the proper address
  //
void memo(byte op) {
  switch(op) {
    case 2:
      for (byte i = 0; i < occBytes; i++) {
        sysSettings[i] = EEPROM.read(addr[i]); 
      } 
      break; 
    default:
      EEPROM.write(addr[op], sysSettings[op]);
      break;         
  }
}

  // ---- Reset by Watchdog Timer
void wdRst() {
  asm volatile ("jmp 0");
//wdt_disable();      // the WDT method works only on boards with new bootloader
//wdt_enable(presc);  // start wd with the prescaller
//while(1) {          // the prescaller time has to expire 
//}    
}

  // ---- Start of System Control & Measurement Functions Sequence
void sysControl() {   
  unsigned long tmrCurr = millis();
  
    // ---- Cyclic System Timer
    //          controls temperature measrement, LEDs, 
    //          the CD4017BE decade counter and display elements
    //
  if ( ((!binFlags[5]) && (tmrInt[1] <= tmrCurr - tmrPrev)) || ( ((binFlags[2]) || (binFlags[4]) || (binFlags[5])) && (copyof_tmrInt0 <= tmrCurr - tmrPrev)) ) {
    tmrPrev = tmrCurr;
    cyclic = !cyclic;
    if (binFlags[5]) {
      ledHandler(2);
    }
    if (binFlags[4]) {
      ledHandler(3);
    }
    if ((!binFlags[2]) || (binFlags[5])) {  // call tempr measurement and related functions
      temprMeas();
      CD4017BE(1);
      graph(0);
      pointer++;
    } 
    if ((pointer == 1) && (!binFlags[2]) && (!binFlags[5])) {  // sending "starting" report
      strSelector(1, 0);
    }
  }  
    
    // ---- Standy Mode Cyclic Analysis 
    //          Waiting for fresh water causes temperature
    //          drop and automatically starts Coffee Making,
    //          if the Autostart procedure is activated.  
  if ((timer0 > tmrInt[2]) && (!binFlags[5])) { 
    if (binFlags[2]) {                             
      temprMeas();
      if ((tempr - temprInit <= temprCheck[sysSettings[1]][0]) && (tempr < temprBottomLim[sysSettings[1]])) { 
        coil(1); 
        strSelector(0, 0);                      
        return;
      }
      else {
        timer0 = 0;                                
      }
      temprInit = tempr;
    }
    
      // ---- One Time Analysis During Coffee Making  
      //          Based on a comparison procedure of the current and reference 
      //          temperature values. Conclusions will be correct under the
      //          declared temperature limit due to the non-linear characteristics of heated mass.
      //          By greather initial temperature only the error check analysis will work.
      //        
    if ((!binFlags[0]) && (!binFlags[1]) && (!binFlags[2]) ) {  
      temprMeas();                                      
      for (byte i = 1; i < (checkSize - 2); i++) {  
        if ((tempr - temprInit <= temprCheck[sysSettings[1]][i]) && ((temprInit <= temprBottomLim[sysSettings[1]]) || (i == 1) || (i == 4))) {
          if ((i == 1) || (i == 4))  {                             
            binFlags[4] = 1;                        
            coil(0);                                
          }
          strSelector(0, i);
          binFlags[0] = 1;
          return;
        }
      }
    }
  }
  
    // ---- Elapsed Time-based Analytics below  
  if ((tempr == temprCheck[sysSettings[1]][5]) && (!binFlags[4])) {  // check if there is sensor error
    binFlags[4] = 1;
    if (!binFlags[5]) {
      coil(0);
    }  
    strSelector(0, 5);
  }
  if (binFlags[5]) {
    return;
  }
  if ((tempr >= temprCheck[sysSettings[1]][6]) && (!binFlags[1])) {  // check boiling point
    coil(0);
    strSelector(0, 6);
  }
  if ((timer0 > tmrInt[3]) && (!binFlags[2]) && (!binFlags[3])) {    // going standby msg AND a second security check
    strSelector(1, 1);
    binFlags[3] = 1;
    if (!binFlags[1]) {                                              // for case if the coil went broken after the One Time Analysis
      coil(0);                                                       //   and therefore there was no boiling point
      strSelector(0, 1);
    }
  }
  if (timer0 > tmrInt[4]) {                                          // the system goes standby
    binFlags[2] = 1;
    strSelector(6, 0);
  }  
}

void temprMeas() { 
  temprPrev = tempr;
  sensors.requestTemperatures();         // update sensor readings
  if (sysSettings[1]) {
    tempr = sensors.getTempCByIndex(0);  // read remperature
  }
  else {
    tempr = sensors.getTempFByIndex(0); 
  }
  if (tempr >= temprPrev + trendSens[sysSettings[1]]) {
    trend[0] = 0;
    trend[1] = 1;
  }
  if (tempr <= temprPrev + trendSens[sysSettings[1]]) {
    trend[0] = 0;
    trend[1] = 0;
  }
  if ((tempr < temprPrev + trendSens[sysSettings[1]]) && (tempr > temprPrev - trendSens[sysSettings[1]])) { 
    trend[0] = 1; 
  }
}

  // ---- Coil Control
  //        0: power off
  //        1: power on
void coil(byte op) {  
  switch (op) {       
    case 0:
      digitalWrite(relay, 0);  
      binFlags[1] = 1;     
      strSelector(1, 2);
      ledHandler(0);
      break;
    case 1:
      digitalWrite(relay, 1);
      strSelector(6, 0);
      CD4017BE(0);
      graph(1);
      for (byte i = 0; i < flagElements; i++) { 
        binFlags[i] = termBool;
      }                 
      pointer = 0;
      timer0 = 0;
      temprMeas();
      temprInit = tempr;
      ledHandler(1);
      break; 
  }
}

void exitTest() {
  temprPrev = tempr;
  binFlags[2] = 1;
  binFlags[5] = 0;
  graph(2);
  ledHandler(0);
  strSelector(1, 4);
}

  // ---- Button Handler Routine
  //        * start coffee making OR 
  //        * interrupt coffee making OR
  //        * exit Test Mode
  //                         
void checkBtn() {             
  if (digitalRead(btn)) {  
    delay(40);             
    if (binFlags[5]) {
      exitTest();
      return;        
    } 
    coil(!digitalRead(relay)); 
  }  
}

  // ---- LED Control
  //        call param
  //          0: grn
  //          1: red
  //          2: grn/red
  //          3: blinking red
void ledHandler(byte op) {       
  switch (op) {                   
    case 0:                       
      analogWrite(A0, bright[0]);  
      analogWrite(A1, bright[1]);
      break;
    case 1: 
      analogWrite(A1, bright[0]);
      analogWrite(A0, bright[1]); 
      break; 
    case 2: 
      ledHandler(cyclic);      
      break;
    case 3: 
      analogWrite(A1, bright[0]);
      analogWrite(A0, bright[cyclic]);       
      break;
  }
}

  // ---- CD4017BE Decade Counter
  //        call param
  //          0: RST (PIN15HIGH)
  //          1: CLK (PIN14HIGH)
  //
void CD4017BE(byte op) {  
  switch (op) {           
    case 0:
      digitalWrite(cd4017beRST, 1);
      delay(1);
      digitalWrite(cd4017beRST, 0);
      break; 
    case 1:
      digitalWrite(cd4017beCLK, 1);
      delay(1);
      digitalWrite(cd4017beCLK, 0);
      break; 
  }     
}

  // ---- Buzzer Handler
  //        call with frequency and delay parameters
void buzz (int b, int d) {  
  for (byte i = 0; i < b * 10; i++) {
    analogWrite(buzzer, b * 1000);
    delay(d * 10);
    analogWrite(buzzer, 0);
  }
}

  // ---- String Handler Function
  //        call param with index or parameter
  //          0: temperature-based analytics       [i]
  //          1: system event reports              [i]
  //          2: serial conn related rep        [conn]
  //          3: flip meas unit bool  [sysSettings[1]]
  //          4: flip autostart bool  [sysSettings[0]]
  //          5: tmrInt[0] val changed  
  //          6: clr str array   
  //                           
void strSelector(byte call, byte i) {  
  str[0] = termStr;                    
  switch (call) {                      
    case 0:                            
      str[0] = checkStr[i];           
      break;                                       
    case 1:                            
      str[0] = reportStr[i];
      break;
    case 2:
      str[0] = connStr[i];
      break;
    case 3:
      str[0] = unitStr[i];
      break; 
    case 4:
      str[0] = autostartStr[i]; 
      break;
    case 5:
      str[0] = tmrInt0Str;
      break;              
    case 6:
      for (byte i = (strSize - 1); i > 0; i--) {
        str[i] = termStr;
      }      
      return;         
  }
  TX();
  for (byte i = (strSize - 1); i > 0; i--) {  // roll for printout
    str[i] = str[i - 1];
  }
  buzz(4, 1); 
}

void draw(void) {               
  if (frame == 0) {
    u8g.drawXBMP( 0, 0, 128, 64, frame0);
  }  
  else {
    if (frame == 1) {
      scr(); 
    }
  }
}

void scr(void) {    
  if (binFlags[2]) {
    #define standbySize 4
    const String standbyStr[standbySize] = {"Press key >>>>", "or", "send", "start"};
    u8g.setFont(u8g_font_courB10);
    u8g.setPrintPos(2, 12);  
    u8g.print(standbyStr[0]);
    u8g.setPrintPos(14, 26);
    u8g.print(standbyStr[1]);
    u8g.setPrintPos(30, 40);
    u8g.print(standbyStr[2]);
    u8g.setColorIndex(!cyclic);    
    u8g.drawBox(74, 28, 50, 14);
    u8g.setColorIndex(cyclic); 
    u8g.setPrintPos(76, 40);
    u8g.print(standbyStr[3]);
    u8g.setColorIndex(1);    
  }
  else {
    if ((posY[0] >= 20) || (posY[13] >= 20)) {
      u8g.drawBitmapP( 5, 0, 1, 12, frame1);
    }
    else {
      u8g.drawBitmapP( 5, 25, 1, 12, frame1);      
    }
    if ((posY[54] >= 30) || (posY[112] >= 30)) {
      u8g.drawHLine(69, 40, 53);
      scrTempr(72, 12);
    }
    else {
      scrTempr(72, 44);
    } 
    for (byte i = 0; i < posYsize; i++) {
      if (posY[i] > 0) {
        u8g.drawVLine(i, posY[i], 2);
      } 
    }  
  }
  u8g.setFont(u8g_font_6x12);
  byte y = 53;
  for (byte i = (strSize - 1); i > 0; i--) {
    u8g.setPrintPos(0, y);
    y = y + 9;
    u8g.print(str[i]);
  } 
}

void scrTempr (byte tX, byte tY) {
  u8g.drawVLine(2, 4, 39);
  u8g.drawHLine(0, 40, 69);
  u8g.drawHLine(0, posY[pointer - 1], 5);
  byte tXrel = 2;
  byte tYrel = 11;  
  if ((tempr < 100) && (tempr > -10)) {          
    u8g.drawFrame(tX - tXrel, tY - tYrel, 45, 13);
    u8g.drawHLine(116, 40, 12);
  }
  else {
    u8g.drawFrame(tX - tXrel, tY - tYrel, 51, 13);
    u8g.drawHLine(122, 40, 6);    
  }
  u8g.setFont(u8g_font_6x12);
  u8g.setPrintPos(tX, tY); 
  u8g.print(tempr);                
  u8g.print(char(176)); 
  u8g.print(unit[sysSettings[1]]);
  if (trend[0]) {
    return;
  }
  tXrel = 12;
  if (trend[1]) {
    u8g.drawBitmapP(tX - tXrel, tY - tYrel, 1, 4, frame2);
  }
  else {
    u8g.drawBitmapP(tX - tXrel, tY - tYrel, 1, 4, frame3);
  }
}

void scrClr(){
  u8g.firstPage(); 
    do {
    } while(u8g.nextPage());
}

  // ---- Maintenance of Graph Arrays 
  //        call param
  //          0: step & fill
  //          1: clr
  //          2: init
void graph(byte op) {                  
  switch (op) {        
    case 0:            
      if (pointer == posYsize) { 
        for (byte i = 0; i < (posYsize - 1); i++) {
          posY[i] = posY[i + 1];
          posY[i + 1] = termByte;
        }
        pointer = posYsize - 1; 
      }
      for (byte i = 0; i < refrElements; i++) { 
        if ((tempr <= temprRefr[i]) && (tempr >= temprRefr[i + 1])) {
          posY[pointer] = i;
          return;
        }
      } 
      break;
    case 1: 
      for (byte i = 0; i < posYsize; i++) { 
        posY[i] = termByte;       
      }
      break;
    case 2:
      float initial = graphRange[sysSettings[1]][binFlags[5]][1];
      for (byte i = 0; i < refrElements; i++) {
        temprRefr[i] = initial;
        initial = initial - graphDiff[sysSettings[1]][binFlags[5]];
      }
      break;  
  }  
}

  // ---- Fill Up Arrays Program Sequence
  //        of reference Fahrenheit temperature values
  //        and calculation of graph vertical steps
  //        called at Startup
  //
void fillArrays() {
  trendSens[0] = convUnit(2, trendSens[1]);
  byte i = 0;
  for (i; i < 5; i++) {
    temprCheck[0][i] = convUnit(2, temprCheck[1][i]);
  }
  for (i; i < 7 ; i++) {
    temprCheck[0][i] = convUnit(1, temprCheck[1][i]);
  }
  temprBottomLim[0] = convUnit(1, temprBottomLim[1]);
  for (byte graph = 0; graph < 2; graph++) {
    for (byte j = 0; j < 2; j++) {
      for (byte k = 0; k < 2; k++) {
        if (graph == 0) {
          graphRange[0][j][k] = convUnit(1, graphRange[1][j][k]);
        }
        else {
          graphDiff[j][k] = (graphRange[j][k][1] - graphRange[j][k][0]) / refrElements;
        }  
      }
    }
  }
}

  // ---- Measurement Unit Conversion Function
  //        call param
  //          0: F2C reference val
  //          1: C2F reference val
  //          2: C2F difference val
float convUnit(byte call, float val) {  
  switch (call) {                       
    case 0:                             
      return ((5 * (val - 32)) / 9);
      break;
    case 1:
      return ((1.8 * val) + 32);
      break;
    case 2:
      return (1.8 * val);
      break;
  }
}

void TX() {
  sUART.println(str[0]);   
}

void checkConn() {
  if ((digitalRead(btState)) != conn) {
    conn = digitalRead(btState); 
    strSelector(2, conn);   
  }
}

  // ---- Accepted Serial Commands 
  //
  //   cmd           descr
  //   [Auto]        : enable / disable autostart with fresh water sensing (stored in EEPROM)
  //   [Reboot]      : restart device  
  //   [Start]       : start coffee makig
  //   [Stop]        : interrupt coffee making OR
  //                   go back to normal working mode from Test Mode
  //   [Test]        : enter Test Mode
  //   [C] / [c]     : Celsius Mode    (stored in EEPROM)
  //   [F] / [f]     : Fahrenheit Mode (stored in EEPROM)
  //   [1]..[999999] : Temperature measurement interval - available in Test Mode only
  //
void RX() {
  for (byte i = 0; i < exRXSize; i++) {
    exRX[i] = termCharSpc;
  }
  while (sUART.available()) {
    for (byte i = 0; i < exRXSize; i++) {
      exRX[i] = sUART.read();
    }
  } 
  if (((exRX[0] == 'A') || (exRX[0] == 'a')) && (exRX[1] == 'u') && (exRX[2] == 't') && (exRX[3] == 'o')) { 
    sysSettings[0] = !sysSettings[0];
    memo(0);
    strSelector(4, sysSettings[0]); 
  }
  if (((exRX[0] == 'R') || (exRX[0] == 'r')) && (exRX[1] == 'e') && (exRX[2] == 'b') && (exRX[3] == 'o') && (exRX[4] == 'o') && (exRX[5] == 't')) {
    if (!binFlags[1]) {
      coil(0);
    } 
    wdRst();
  }  
  if (((exRX[0] == 'S') || (exRX[0] == 's')) && (exRX[1] == 't')) {
    if ((exRX[2] == 'a') && (exRX[3] == 'r') && (exRX[4] == 't')) { 
      if (binFlags[5]) {
        exitTest();
      }
      coil(1);
    }
    if ((exRX[2] == 'o') && (exRX[3] == 'p')) { 
      if (binFlags[5]) {
        exitTest();
      }
      else {
        coil(0);
      }
    }
  }
  if (((exRX[0] == 'T') || (exRX[0] == 't')) && (exRX[1] == 'e') && (exRX[2] == 's') && (exRX[3] == 't')) {
    if (!binFlags[1]) {
      coil(0);
    }  
    strSelector(6, 0);
    strSelector(1, 3);
    binFlags[2] = 0;    
    binFlags[4] = 0;    
    binFlags[5] = 1;
    graph(1);
    pointer = 0;
    graph(2);
  } 
  if ((!sysSettings[1]) && ((exRX[0] == 'C') || (exRX[0] == 'c'))) {
    sysSettings[1] = 1;
    temprInit = convUnit(0, temprInit);      
    temprPrev = convUnit(0, temprPrev);
    tempr = convUnit(0, tempr);
    goto changeUnit;
  }
  if ((sysSettings[1]) && ((exRX[0] == 'F') || (exRX[0] == 'f'))) {
    sysSettings[1] = 0;
    temprInit = convUnit(1, temprInit);
    temprPrev = convUnit(1, temprPrev);
    tempr = convUnit(1, tempr);
    goto changeUnit;
  }  
  if ((binFlags[5]) && ((exRX[0] - '0') > 0) && (atof(exRX) >= 1) && (atof(exRX) <= 999999)) {
      copyof_tmrInt0 = (atof(exRX) * 1000);
      strSelector(5, 0); 
  }  
  for (byte i = 0; i < exRXSize; i++) {
    exRX[i] = termCharSpc;
  }
  sUART.flush(); 
  return;
  changeUnit:
    memo(1);      
    graph(2);
    strSelector(3, sysSettings[1]);
}

Credits

Gyula Ősi

Gyula Ősi

4 projects • 33 followers

Comments