/*
_____ _______ _
| __ \ |__ __| | |
| |__) |__ _ ___ ___| |_ __ __ _ ___| | ___ __
| _ // _` |/ __/ _ \ | '__/ _` |/ __| |/ / '__|
| | \ \ (_| | (_| __/ | | | (_| | (__| <| |
|_| \_\__,_|\___\___|_|_| \__,_|\___|_|\_\_|
Program: WheelHub
Author: Tom Jesper
Purpose: Connects to modified eChook board via I2C to allow
for driver input and feedback.
*/
/*------------------------------------------------------------
INCLUDES
------------------------------------------------------------*/
#include <Arduino.h>
#include <Bounce2.h>
#include <U8glib.h>
#include "frames.h"
#include "logos.h"
/*------------------------------------------------------------
OLED SETTINGS
------------------------------------------------------------*/
#define OLED_RESET 4
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
const int NUMBER_FRAMES = 5;
#define TOP_BORDER_SIZE 8
#define USABLE_SCREEN_HEIGHT (SCREEN_HEIGHT - TOP_BORDER_SIZE)
#define INDICATOR_RADIUS 2
#define INDICATOR_Y_SPACING_PX (USABLE_SCREEN_HEIGHT / (NUMBER_FRAMES + 1))
#define INDICATOR_TOTAL_Y ((NUMBER_FRAMES - 1) * INDICATOR_Y_SPACING_PX + (2 * INDICATOR_RADIUS))
#define INDICATOR_Y_START ((USABLE_SCREEN_HEIGHT / 2 + TOP_BORDER_SIZE) - INDICATOR_TOTAL_Y / 2)
#define SCREEN_REFRESH_RATE 250;
/*------------------------------------------------------------
FRAME SETTINGS
------------------------------------------------------------*/
frame FRAMES[NUMBER_FRAMES] = {voltage, current, amphour, mode, pwm};
int currentFrame = 0;
int unitsXOffset;
bool debug = true;
// U8GLIB_SH1106_128X64 u8g(U8G_I2C_OPT_NONE); // I2C / TWI
// U8GLIB_SH1106_128X64 u8g(U8G_I2C_OPT_DEV_0|U8G_I2C_OPT_FAST); // Dev 0, Fast I2C / TWI
U8GLIB_SH1106_128X64 u8g(U8G_I2C_OPT_NO_ACK); // Display which does not send ACK
Bounce SCREEN_BUTTON_A = Bounce();
Bounce SCREEN_BUTTON_B = Bounce();
Bounce FUNCTION_BUTTON = Bounce();
Bounce ALL_BUTTONS[3] = {SCREEN_BUTTON_A, SCREEN_BUTTON_B, FUNCTION_BUTTON};
bool FRAME_CHANGE_FLAG = false;
bool FORCE_FRAME_UPDATE = false;
bool FRAME_UPDATE_FLAG = false;
byte POT_UPDATE_RATE = 500;
unsigned int lastScreenRefresh = 0;
unsigned int LAST_POT_UPDATE = 0;
void setup(void)
{
Serial.begin(9600);
pinMode(A0, INPUT);
pinMode(A1, INPUT_PULLUP);
pinMode(A2, INPUT_PULLUP);
pinMode(A3, INPUT_PULLUP);
SCREEN_BUTTON_A.attach(A1);
SCREEN_BUTTON_B.attach(A2);
FUNCTION_BUTTON.attach(A3);
SCREEN_BUTTON_A.interval(50);
SCREEN_BUTTON_B.interval(50);
FUNCTION_BUTTON.interval(50);
u8g.setColorIndex(1); // pixel on
FORCE_FRAME_UPDATE = true;
}
void loop(void)
{
// check buttons
SCREEN_BUTTON_A.update();
SCREEN_BUTTON_B.update();
FUNCTION_BUTTON.update();
// functionChange = checkFunctionButton();
if (SCREEN_BUTTON_A.fell())
{
// Serial.println("A");
FRAME_CHANGE_FLAG = true;
currentFrame--;
if (currentFrame < 0)
{
currentFrame = 0;
}
// Serial.println(currentFrame);
}
if (SCREEN_BUTTON_B.fell())
{
// Serial.println("B");
FRAME_CHANGE_FLAG = true;
currentFrame++;
if (currentFrame > NUMBER_FRAMES - 1)
{
currentFrame = NUMBER_FRAMES - 1;
}
// Serial.println(currentFrame);
}
if (FUNCTION_BUTTON.changed())
{
// THE STATE OF THE INPUT CHANGED
// GET THE STATE
int deboucedInput = FUNCTION_BUTTON.read();
// IF THE CHANGED VALUE IS HIGH
if (deboucedInput == HIGH)
{
if (FUNCTION_BUTTON.previousDuration() > 1500)
{
FRAME_CHANGE_FLAG = true;
switch (FRAMES[currentFrame].id)
{
case 'v':
break;
case 'a':
break;
case 'c':
break;
case 'm':
switch (mode.value)
{
case 0:
mode.value = 2;
break;
case 1:
mode.value = 2;
break;
case 2:
mode.value = 0;
break;
case 3:
break;
}
}
}
else
{
FRAME_CHANGE_FLAG = true;
switch (FRAMES[currentFrame].id)
{
case 'v':
break;
case 'a':
break;
case 'c':
break;
case 'm':
switch (mode.value)
{
case 0:
mode.value = 1;
break;
case 1:
mode.value = 0;
break;
case 2:
break;
case 3:
break;
}
}
}
}
}
if (mode.value == 1)
{
if (millis() - LAST_POT_UPDATE > POT_UPDATE_RATE)
{
LAST_POT_UPDATE = millis();
// Serial.println(analogRead(A0));
mode.value2 = map(analogRead(A0), 0, 1023, -100, 100);
if (FRAMES[currentFrame].id == 'm')
FRAME_CHANGE_FLAG = true;
}
}
if (debug) {
{
if (millis() - LAST_POT_UPDATE > POT_UPDATE_RATE)
{
if (map(analogRead(A0), 0, 1023, FRAMES[currentFrame].limitLower, FRAMES[currentFrame].limitUpper) != FRAMES[currentFrame].value)
{ LAST_POT_UPDATE = millis();
// Serial.println(analogRead(A0));
FRAMES[currentFrame].value = map(analogRead(A0), 0, 1023, FRAMES[currentFrame].limitLower, FRAMES[currentFrame].limitUpper);
FRAME_CHANGE_FLAG = true;
// Serial.println(voltage.value);
FRAME_CHANGE_FLAG = true;
}
}
}
}
// picture loop
if (FRAME_CHANGE_FLAG || FORCE_FRAME_UPDATE || FRAME_UPDATE_FLAG)
{
u8g.firstPage(); // required for u8g library
int current_u8g_page = 0; // reset the u8g page number
lastScreenRefresh = millis();
do
{ //
u8g.setFont(u8g_font_orgv01); // set smaller font for tickmarks
if (current_u8g_page == 0)
{
// Redraw the top banner if the frame is changed
if (FRAME_CHANGE_FLAG || FORCE_FRAME_UPDATE)
{
u8g.drawBox(0, 0, 128, 64);
u8g.setColorIndex(0);
u8g.setFontPosCenter();
// u8g.drawStr(64 - int(u8g.getStrPixelWidth(FRAMES[currentFrame].title.c_str()) / 2), 5, FRAMES[currentFrame].title.c_str());
u8g.drawStr(64 - int(u8g.getStrPixelWidth(FRAMES[currentFrame].title) / 2), 5, FRAMES[currentFrame].title);
FRAME_CHANGE_FLAG = false;
FORCE_FRAME_UPDATE = false;
}
}
// Redraw the information section of the frame if there is new data to display
if (current_u8g_page > 0 && current_u8g_page < 7)
{
u8g.setFont(u8g_font_freedoomr25n);
u8g.setColorIndex(1);
u8g.setFontPosTop();
switch (FRAMES[currentFrame].id)
{
case 'v':
u8g.drawStr(64 - int(u8g.getStrPixelWidth(String(FRAMES[currentFrame].value / 10.0, 1).c_str()) / 2), 12, String(FRAMES[currentFrame].value / 10.0, 1).c_str());
u8g.setFont(u8g_font_unifont);
u8g.drawStr(110, 35, FRAMES[currentFrame].unit);
break;
case 'a':
u8g.drawStr(64 - int(u8g.getStrPixelWidth("00.0") / 2), 12, String(FRAMES[currentFrame].value / 10.0, 1).c_str());
u8g.setFont(u8g_font_unifont);
u8g.drawStr(110, 35, FRAMES[currentFrame].unit);
break;
case 'c':
u8g.drawStr(64 - int(u8g.getStrPixelWidth("00.0") / 2), 12, String(FRAMES[currentFrame].value / 10.0, 1).c_str());
u8g.setFont(u8g_font_unifont);
u8g.drawStr(110, 35, FRAMES[currentFrame].unit);
break;
case 'm':
u8g.setFont(u8g_font_fub20);
u8g.setFontPosTop();
switch (mode.value)
{
case 0:
u8g.drawStr(64 - int(u8g.getStrPixelWidth("AUTO") / 2), 12, "AUTO");
break;
case 1:
u8g.drawStr(64 - int(u8g.getStrPixelWidth("TURBO") / 2), 12, "TURBO");
u8g.setFont(u8g_font_orgv01);
u8g.drawStr(64 - int(u8g.getStrPixelWidth(String("Target: " + String(mode.value2 / 10.0, 1)).c_str()) / 2), 40, String("Target: " + String(((current.target + mode.value2) / 10.0), 1)).c_str());
u8g.drawStr(64 - int(u8g.getStrPixelWidth(String(mode.value2 / 10.0, 1).c_str()) / 2), 50, String(mode.value2 / 10.0, 1).c_str());
// Serial.println(mode.value2);
u8g.drawBox(20, 55, 128 - 20 * 2, 5);
u8g.setColorIndex(0);
u8g.drawBox(21, 55, 128 - 21 * 2, 4);
u8g.setColorIndex(1);
u8g.drawBox(22, 55, map(mode.value2, -100, 100, 0, 128 - (22 * 2)), 3);
break;
case 2:
u8g.drawStr(64 - int(u8g.getStrPixelWidth("MANUAL") / 2), 12, "MANUAL");
break;
}
break;
case 'p':
u8g.drawStr(64 - int(u8g.getStrPixelWidth(String(FRAMES[currentFrame].value).c_str()) / 2), 12, String(FRAMES[currentFrame].value).c_str());
u8g.setFont(u8g_font_unifont);
u8g.drawStr(110, 35, FRAMES[currentFrame].unit);
break;
}
for (int i = 0; i < NUMBER_FRAMES; i++)
{
if (i == currentFrame)
{
u8g.drawDisc(5, INDICATOR_Y_START + (i * INDICATOR_Y_SPACING_PX), INDICATOR_RADIUS);
}
else
{
u8g.drawCircle(5, INDICATOR_Y_START + (i * INDICATOR_Y_SPACING_PX), INDICATOR_RADIUS);
}
}
}
current_u8g_page++; // increment the current number counter
;
} while (u8g.nextPage());
FORCE_FRAME_UPDATE = false;
FRAME_UPDATE_FLAG = false;
}
}