The Kokkorobox

- 9 mins read

Series: Desky

The box was made as an experiment to see if something like this is a good idea. Maybe we should call it a NodeMCU ESP8266 running an OLED SPI monitor on a 18650 battery with USB charge controller, some buttons, and a custom 3D-printed case just to nerd out as much as possible :)

image

The box is connected to Wi-Fi with an ESP8266 chip and can send out updates to a wallboard. It is supposed to be dynamically updated by MQTT, but it is not implemented yet. You can send messages to the lowest line, and it will display the message and flash an LED until you sign off on the message with an MQTT message of 1. To save battery, it will sleep after 30 minutes if there has been no action on button 1. It starts so fast that there is no reason to keep it alive longer unless you want the MQTT messages, but we don’t really need those at the moment. It will also count down the battery on the top right side when it reaches below 3.3V. It will go from 244 to 0 and turn off at 2.5V because the voltage sensor can only handle a max of 3.3V. Charging is done on the top USB, and it will have a red LED until it’s full. You can also power it from both USB ports.

Other than that, it uses an HTTP GET to push updates to an API I made on the wallboard.

image

Stuff that could still be done: add updates via MQTT—the API should be able to handle whatever as long as I can alter it. Create a new back panel with holes for the two USB ports, and see if I can design a new box that has better access to the buttons and screen. Add more functionality it can do…

So this is the mess of code.

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <PubSubClient.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>


/*
  static const uint8_t SDA = 4;
  static const uint8_t SCL = 5;

  static const uint8_t LED_BUILTIN = 16;
  static const uint8_t BUILTIN_LED = 16;

  static const uint8_t D0   = 16;
  static const uint8_t D1   = 5;
  static const uint8_t D2   = 4;
  static const uint8_t D3   = 0;
  static const uint8_t D4   = 2;
  static const uint8_t D5   = 14;
  static const uint8_t D6   = 12;
  static const uint8_t D7   = 13;
  static const uint8_t D8   = 15;
  static const uint8_t D9   = 3;
  static const uint8_t D10  = 1;
*/

// If using software SPI (the default case):   please stick to these pin numbers unless you have some other board
//Pin connections for OLED are as follows - (D5, D7, D3, D4, 12); // (SCL,SDA,RES,DC,CS)

ADC_MODE(33); //voltage check thingy
const int BUTTON_PIN = D1;//button is connected to GPIO pin D1
const int BUTTON_PIN2 = D2;
const int BUTTON_PIN3 = D8;
const int SOUND_PIN = D6;
boolean buttonState = false;        // variable for reading the pushbutton status
void handleKey1() {
  buttonState = true;  
}
int buttonState2 = 0;
boolean buttonState3 = false;
void handleKey3() {
  buttonState3 = true;  
}
int sleepTimer = 0;
long lastUpdateMillis = 0;
// Update these with values suitable for your network.
const char* ssid = "asd";
const char* password = "asd";
const char* mqtt_server = "asd";
const char* userName = "asd";
const char* passWord = "asd";
//bleh
int hasSentStringToServer = 0;
int choice1 = 0;
int choice2 = 0;
String CName = "";
unsigned long timez;

WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[50];
int value = 0;
String TopText = "Teknisk";
String BottomText = "UP=Change MID=Send";
//volts
const int ADC_PIN = A0;
const float ANALOG_TO_VOLTAGE_FACTOR = 3.29/1001.0; //0.00322265625
int rawVoltValue;
float voltage;
//blinking
boolean blinkOn = false;
int lastUpdateMillisBlink=0;
void setup_wifi() {
  delay(100);
  // We start by connecting to a WiFi network
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  randomSeed(micros());
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void callback(char* topic, byte* payload, unsigned int length) {
  String dispText = "";
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
    dispText += (char)payload[i];
  }
  Serial.println();
  
      String msg = "1";
      digitalWrite(BUILTIN_LED, HIGH);
      char message[58];
      msg.toCharArray(message, 58);
      client.publish("teknisk/kokoro/ok", message);
      
  // Switch on the LED if an 1 was received as first character
  /* later :)
  if ((char)payload[0] == '1') {
    digitalWrite(BUILTIN_LED, LOW);   // Turn the LED on (Note that LOW is the voltage level
    // but actually the LED is on; this is because
    // it is acive low on the ESP-01)
  } else {
    digitalWrite(BUILTIN_LED, HIGH);  // Turn the LED off by making the voltage HIGH
  }
  */
  //digitalWrite(BUILTIN_LED, LOW);   // Turn the LED on (Note that LOW is the voltage level
  blinkOn = true;
  //String dispText = String((char*)payload);
  BottomText = dispText;
  Serial.print(dispText);
}




void reconnect() {
  // Loop until we're reconnected
  while (!client.connected())
  {
    Serial.print("Attempting MQTT connection...");
    // Create a random client ID
    String clientId = "Teknisk_wallb_changer-";
    clientId += String(random(0xffff), HEX);
    // Attempt to connect
    //if (client.connect(clientId.c_str())) //use if no pass
    if (client.connect(clientId.c_str(), userName, passWord))
    {
      Serial.println("connected");
      //once connected to MQTT broker, subscribe command if any
      client.subscribe("teknisk/kokoro/display");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
} //end reconnect()




//oled stuff
#define OLED_SDA   D7  //MOSI
#define OLED_SCL   D5  //CLK
#define OLED_DC    D4  //
#define OLED_CS    12  // no need of connecting, just use some pin number
#define OLED_RESET D3  //RES
Adafruit_SSD1306 display(OLED_SDA, OLED_SCL, OLED_DC, OLED_RESET, OLED_CS);     // constructor to call OLED display using adafruit library

#define LOGO16_GLCD_HEIGHT 16
#define LOGO16_GLCD_WIDTH  16
static const unsigned char PROGMEM logo16_glcd_bmp[] =    // // since i am using adafruit library, i have to display their logo
{ B00000000, B11000000,
  B00000001, B11000000,
  B00000001, B11000000,
  B00000011, B11100000,
  B11110011, B11100000,
  B11111110, B11111000,
  B01111110, B11111111,
  B00110011, B10011111,
  B00011111, B11111100,
  B00001101, B01110000,
  B00011011, B10100000,
  B00111111, B11100000,
  B00111111, B11110000,
  B01111100, B11110000,
  B01110000, B01110000,
  B00000000, B00110000
};

//#if (SSD1306_LCDHEIGHT != 64)
//#error("Height incorrect, please fix Adafruit_SSD1306.h!");
//#endif

void setup()   {
  pinMode(BUILTIN_LED, OUTPUT);     // Initialize the BUILTIN_LED pin as an output
  digitalWrite(BUILTIN_LED, HIGH);
  Serial.begin(115200);
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  pinMode(BUTTON_PIN2, INPUT_PULLUP);
  pinMode(BUTTON_PIN3, INPUT_PULLUP);
  attachInterrupt(BUTTON_PIN, handleKey1, RISING);
  attachInterrupt(BUTTON_PIN3, handleKey3, RISING);



  display.begin(SSD1306_SWITCHCAPVCC);   // since i am using adafruit library, i have to display their logo
  // init done
  // Show image buffer on the display hardware.
  // Since the buffer is intialized with an Adafruit splashscreen
  // internally, this will display the splashscreen.
  display.display();
  delay(500);
  // Clear the buffer.
  display.clearDisplay();
  // NOTE: You _must_ call display after making any drawing commands
  // to make them visible on the display hardware!

  // following OPTIONAL part is just to display the message only during the startup--- you can modify or just remove it
  display.setTextColor(WHITE);
  display.setTextSize(1);
  display.setCursor(25, 11);
  display.print("Hakon Yndestad");
  //  display.print("Temp. reading");
  display.display();
  display.setCursor(25, 0);
  display.print("Kokkorobox");
  display.display();
  delay(4000);
  display.clearDisplay();
  // OPTIONAL part ends here
   
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
  int status;
  long now = millis();

  if ( choice1 == 0) {
    CName = "PerMorten";
  } else if ( choice1 == 1 ) {
    CName = "Henrik";
  } else if ( choice1 == 2 ) {
    CName = "Anders";
  } else if ( choice1 == 3 ) {
    CName = "David";
  } else if ( choice1 == 4 ) {
    CName = "Morten";
  } else if ( choice1 == 5 ) {
    CName = "Ken";
  } else if ( choice1 == 6 ) {
    CName = "Hakon";
  } else {
    CName = "Aulin";
  }


  // read the state of the pushbutton value:
  buttonState = digitalRead(BUTTON_PIN);
  if (buttonState && millis() - lastUpdateMillis > 250) {
    String msg = "Button status: ";
    buttonState = false;
    lastUpdateMillis = millis(); //doubbletap reset
    sleepTimer=millis(); //update sleeptimer
      choice1 += 1;
      msg = "ON";
      char message[58];
      msg.toCharArray(message, 58);
      Serial.println(message);
      Serial.println(choice1);
      Serial.println(CName);
      //publish sensor data to MQTT broker
      //client.publish("hass/node/state", message);
      if (choice1 > 8) {
        choice1 = 0;
      }
  }

  // check if the pushbutton2 is pressed.
  // if it is, the buttonState2 is HIGH:
  buttonState2 = digitalRead(BUTTON_PIN2);
  if (buttonState2 == HIGH && hasSentStringToServer == 0) {
    String msg = "Button status: ";
    if (buttonState2 == HIGH )
    {
      digitalWrite(BUILTIN_LED, LOW);
      String APIurl= "http://asd"; //for wallboard
      msg = "ON2- Sending command to server";
      char message[58];
      msg.toCharArray(message, 58);
      Serial.println(message);
      Serial.println(choice1);
      Serial.println(CName);
      Serial.println("----------");
      BottomText = "Sendt " + CName;
      //publish sensor data to MQTT broker
      HTTPClient http;  //Declare an object of class 
      APIurl += "99/";
      APIurl += CName;
      APIurl += "/";
      APIurl += choice1;
      http.begin(APIurl);  //Specify request destination (http://blablabla)
      int httpCode = http.GET();                      //Send the request
      if (httpCode > 0) {                            //Check the returning code
        String payload = http.getString();          //Get the request response payload
        Serial.println(payload);                     //Print the response payload
      }

      http.end();   //Close connection
      Serial.println(APIurl);
      Serial.println("----------");
      //client.publish("hass/node/state", message);
      hasSentStringToServer == 1;
      delay(250); //delay so that the itchy button dont doubbletapp
      digitalWrite(BUILTIN_LED, HIGH);
    }

    if (buttonState2 == LOW )
    {
      hasSentStringToServer = 0;
      delay(250); //delay so that the itchy button dont doubbletapp
    }
  }







 // Button 3 stuff
  buttonState3 = digitalRead(BUTTON_PIN3);
  if (buttonState3 && millis() - lastUpdateMillis > 250) {
    buttonState3 = false;
    lastUpdateMillis = millis();
      digitalWrite(BUILTIN_LED, LOW);
      
      //publish accept data to MQTT broker
      String msg = "Kvittert: ";
      digitalWrite(BUILTIN_LED, HIGH);
      char message[58];
      msg.toCharArray(message, 58);
      client.publish("teknisk/kokoro/ok", message);
     
      Serial.println("---------- high");
      delay(250); //delay so that the itchy button dont doubbletapp
      blinkOn= false;
  }
/*
    if (buttonState3 == LOW )
    {
      Serial.println("---------- low");
      digitalWrite(BUILTIN_LED, LOW);
      delay(250); //delay so that the itchy button dont doubbletapp
    }
*/  

//led blinker
 if (blinkOn && millis() - lastUpdateMillisBlink > 250){
  if(digitalRead(BUILTIN_LED)){
    digitalWrite(BUILTIN_LED, LOW);
  }else{
    digitalWrite(BUILTIN_LED, HIGH);
  }
    lastUpdateMillisBlink = millis();
 }

  //voltage check thingy
  rawVoltValue = analogRead(ADC_PIN);
  voltage = rawVoltValue * ANALOG_TO_VOLTAGE_FACTOR;

//run display
  TopDisplayText(TopText);
  TopDisplayBattery(rawVoltValue-780);
  MiddleDisplayText(CName);
  BottomDisplayText(BottomText);

 
//set to sleep if idle for 30minutes or battery is below 2.5v
  if (millis() - sleepTimer > 1800000 || rawVoltValue < 780)
    {
  Serial.println(sleepTimer);
  ESP.deepSleep(0); // 20e6 is 20 microseconds 0 forever
  sleepTimer=millis(); //resets timer if we deside to auto wake later
    }


  
}//void loop

void TopDisplayBattery(int rawVoltValue) {
  display.setTextColor(WHITE);   
  display.setTextSize(1);
  display.setCursor(110, 0);
  display.print(rawVoltValue);
  display.setCursor(100, 0);
  display.print("B");
}

void TopDisplayText(String TopText) {
  display.setTextColor(WHITE);   
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.print(TopText);   
}
void BottomDisplayText(String BottomText) {
  display.setTextColor(WHITE);   
  display.setTextSize(1);
  display.setCursor(0, 20);
  display.print(BottomText);   
}

void MiddleDisplayText(String text) {
  //display.drawRect(1, 1, display.width() - 1, display.height() - 1, WHITE); // draws the outer rectangular boundary on the screen
  display.setTextColor(WHITE);  //if multicollor OLED
  display.setTextSize(1);
  display.setCursor(0, 10);
  display.print(text);   
  display.display();
  display.clearDisplay();
}