Peter Scargill Experimental NANO OLED based Power Supply Control

Created by Peter Scargill last modified
#include <Wire.h>
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"
#include <EEPROM.h>

struct
{
  float highV;
  float lowV;
  uint16_t check;
} myVars;

// 0X3C+SA0 - 0x3C or 0x3D
#define I2C_ADDRESS 0x3C

SSD1306AsciiWire oled;

#define LIGHT 12
#define RELAY 11
#define WARNING 10

#define WAITING 60
#define ONDELAY 20

#define SWITCHON 3.7
#define SWITCHOFF 3.1

#define SET 5
#define UP 6
#define DOWN 7

uint32_t secs = 0;
uint32_t upSecs = 0;
uint32_t downSecs = 0;

void LcdP(char *fmt, ...)
{
  char buf[128]; // resulting string limited to 128 chars
  va_list args;
  va_start(args, fmt);
  vsnprintf(buf, 128, fmt, args);
  va_end(args);
  oled.print(buf);
}

uint32_t mymillis = 0;
uint32_t my100millis = 0;
uint8_t lightState = 0;
uint8_t state = 0; // 0 is power up, 1 is on, 2 is turning off
float vol;
uint8_t onDelay = 0;

uint16_t lightOnSet = 2;
uint16_t lightOffSet = 1022;

float average = 0;

uint8_t updateTimeout = 0;

char *theState = "standby";

uint8_t instate = 0;
uint8_t updated = 0;
uint8_t sKeypressed = 0;
uint8_t uKeypressed = 0;
uint8_t dKeypressed = 0;

float voltage()
{
  // read the input on analog pin 0
  int sensorValue = analogRead(A0);
  // Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
  float vols = sensorValue * (5.0 / 1023.0);
  // print out the value you read:
  return (vols);
}

void p(char *fmt, ...)
{
  char buf[128]; // resulting string limited to 128 chars
  va_list args;
  va_start(args, fmt);
  vsnprintf(buf, 128, fmt, args);
  va_end(args);
  Serial.print(buf);
}

void gotoXY(int x, int y)
{
  oled.setCursor(x, y);
}

//------------------------------------------------------------------------------
void setup()
{
  EEPROM.get(0, myVars);
  if (myVars.check != 0x52d2)
  {
    myVars.check = 0x52d2;
    myVars.highV = SWITCHON;
    myVars.lowV = SWITCHOFF;
    EEPROM.put(0, myVars);
  }
  Wire.begin();
  oled.begin(&Adafruit128x64, I2C_ADDRESS);
  oled.set400kHz();
  oled.setFont(Adafruit5x7);
  uint32_t m = micros();

  Serial.begin(57600);
  digitalWrite(LIGHT, LOW);
  digitalWrite(RELAY, LOW);
  digitalWrite(WARNING, LOW);
  pinMode(LIGHT, OUTPUT);
  pinMode(RELAY, OUTPUT);
  pinMode(WARNING, OUTPUT);
  pinMode(SET, INPUT_PULLUP);
  pinMode(UP, INPUT_PULLUP);
  pinMode(DOWN, INPUT_PULLUP);

  average = voltage();

  oled.clear();
  LcdP("Init....\r\n");
  //oled.println("Hello world!");
  // oled.println("A long line");
  //oled.println();
  //oled.set2X();
  // oled.println("2X demo");
  // oled.set1X();
  //oled.print("\nmicros: ");
  // oled.print(micros() - m);
  my100millis = millis() + 1000;
}
//------------------------------------------------------------------------------
void loop()
{

  // put your main code here, to run repeatedly:

  if (my100millis <= millis()) // 100ms for key check
  {
    my100millis += 100;
    if (digitalRead(SET) == 0)
    {
      if (sKeypressed == 0)
      {
        sKeypressed = 1;
        instate++;
        updated = 0;
        updateTimeout = 100;
      }
    }
    else
      sKeypressed = 0;
    if (digitalRead(UP) == 0)
    {
      if (uKeypressed == 0)
      {
        uKeypressed = 1;
        if (instate == 1)
          myVars.highV += 0.1;
        else if (instate == 2)
          myVars.lowV += 0.1;
        updated = 0;
        updateTimeout = 100;
      }
    }
    else
      uKeypressed = 0;
    if (digitalRead(DOWN) == 0)
    {
      if (dKeypressed == 0)
      {
        dKeypressed = 1;
        if (instate == 1)
          myVars.highV -= 0.1;
        else if (instate == 2)
          myVars.lowV -= 0.1;
        updated = 0;
        updateTimeout = 100;
      }
    }
    else
      dKeypressed = 0;
      switch (instate)
      {
      case 0:  if (updated == 0)
                {
                  gotoXY(0, 1);
                  oled.clearToEOL();
                  updated = 1;
                }
                break;
     case 1:  if (updated == 0)
                {
                  gotoXY(0, 1);
                  LcdP("SET: High V=%d.%d   ", int(myVars.highV), ((int(myVars.highV * 100)) % 100) / 10); oled.clearToEOL();
                  updated = 1;
                }
                break;
     case 2:  if (updated == 0)
                {
                  gotoXY(0, 1);
                  LcdP("SET: Low  V=%d.%d   ", int(myVars.lowV), ((int(myVars.lowV * 100)) % 100) / 10); oled.clearToEOL();
                  updated = 1;
                }
                break;
     case 3:  if (updated == 0)
                {
                  gotoXY(0, 1);
                  LcdP("Version 1.01"); oled.clearToEOL();
                  updated = 1;
                }
                break;               
    case 4:  updated=0; instate=0; break;     
      }
  
    if (updateTimeout)
    {
      if (--updateTimeout == 0)
      {
        instate = 0;
        updated = 0;
        EEPROM.put(0, myVars);
      }
    }
  }

  if (mymillis <= millis())
  {
    vol = voltage();
    average += (vol / 16.0);
    average = average * 16.0 / 17.0;
    vol = average;
    if (state == 0)
    {
      if (vol > myVars.highV)
      {
        if (onDelay == 0)
        {
          onDelay = ONDELAY * 2;
          theState = "starting";
        }
      }
    }
    else if (state == 1)
    {
      if (vol < myVars.lowV)
      {
        state = (WAITING * 2) + 2;
        theState = "warning ";
      }
    }
    else if (state ==2)
    {
       digitalWrite(WARNING, LOW);
    }
    else if (state > 2)
    {
      digitalWrite(WARNING, HIGH);
      state--;
      if (state == 2)
      {
        state = 0;
        digitalWrite(RELAY, LOW);
        digitalWrite(WARNING, LOW);
        downSecs = 0;
        //analogWrite(PIN_PWM,255); // only 3v3 intended so this will likely do
        theState = "standby ";
      }
    }
    else
      digitalWrite(WARNING, LOW);

    if (vol > myVars.highV)
    {
      if (onDelay)
      {
        if (--onDelay == 0)
        {
          state = 1;
          //analogWrite(PIN_PWM,128); // only 3v3 intended so this will likely do
          theState = "active  ";
          upSecs = 0;
        }
      }
    }
    else
    {
      if (onDelay)
      {
        onDelay = 0;
        theState = "standby ";
        state = 0;
      }
    }

    if (lightState == 0)
    {
      p("{\"voltage\":");
      Serial.print(vol);
      p(",\"status\":\"%s\"}\r\n", theState); // sadly printf here does not support floats
      oled.clearToEOL();
      gotoXY(0, 0);
      LcdP("POWER SUPPLY CONTROL"); oled.clearToEOL();
 
      gotoXY(0, 2);
      LcdP("Current volts: %d.%dv", int(vol), ((int(vol * 100)) % 100) / 10); oled.clearToEOL();
      gotoXY(0, 3);
      LcdP("State: %s", theState); oled.clearToEOL();
      gotoXY(0, 4);
      LcdP("High: %d.%dv Low: %d.%dv", int(myVars.highV), ((int(myVars.highV * 100)) % 100) / 10, int(myVars.lowV), ((int(myVars.lowV * 100)) % 100) / 10); oled.clearToEOL();
      gotoXY(0, 5);
      LcdP("On:  %04ldd %02ld:%02ld:%02ld",             secs/86400,(upSecs / 3600) % 24, (upSecs % 3600) / 60, upSecs % 60); oled.clearToEOL();
      gotoXY(0, 6);
      LcdP("Off: %04ldd %02ld:%02ld:%02ld",             secs/86400,(downSecs / 3600) %24 , (downSecs % 3600) / 60, downSecs % 60); oled.clearToEOL();
      gotoXY(0, 7);
      LcdP("Tot: %04ldd %02ld:%02ld:%02ld",             secs/86400,(secs / 3600) % 24, (secs % 3600) / 60, secs % 60); oled.clearToEOL();

      lightState = 1;
      digitalWrite(LIGHT, HIGH);
      switch (state)
      {
      case 0:
        if (onDelay)
        {
          lightOnSet = 80;
          lightOffSet = 944;
        }
        else
        {
          lightOnSet = 1;
          lightOffSet = 1023;
        }
        digitalWrite(RELAY, LOW);
        break; // battery is low
      case 1:
        lightOnSet = 980;
        lightOffSet = 44;
        digitalWrite(RELAY, HIGH);
        break; // battery is ok
      default:
        lightOnSet = 512;
        lightOffSet = 512;
        break; // intermediate state;
      }
      mymillis = millis() + lightOnSet;
    }
    else
    {
      lightState = 0;
      digitalWrite(LIGHT, LOW);
      mymillis = millis() + lightOffSet;
      secs++;
      if (state == 1)
        upSecs++;
      else
        downSecs++;
    }
  }
}

Comments (0)