Snippets

Peter Scargill Experimental NANO OLED based Power Supply Control

You are viewing an old version of this snippet. View the current version.
Revised by Peter Scargill c77f2a9
// See blog entry here http://tech.scargill.net/gpio-the-hard-way/

#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;
      }
    }
    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;

    if (instate == 0)
    {
      if (updated == 0)
      {
        gotoXY(0, 1);
        LcdP("                    ");
        updated = 1;
      }
    }
    if (instate == 1)
    {
      if (updated == 0)
      {
        gotoXY(0, 1);
        LcdP("SET: High V=%d.%d   ", int(myVars.highV), ((int(myVars.highV * 100)) % 100) / 10);
        updated = 1;
      }
    }
    if (instate == 2)
    {
      if (updated == 0)
      {
        gotoXY(0, 1);
        LcdP("SET: Low  V=%d.%d   ", int(myVars.lowV), ((int(myVars.lowV * 100)) % 100) / 10);
        updated = 1;
      }
    }
    if (instate == 3)
    {
      updated = 0;
      instate = 0;
    }

    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, HIGH);
      state--;
      if (state == 2)
      {
        state = 0;
        digitalWrite(RELAY, 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

      gotoXY(0, 0);
      LcdP("POWER SUPPLY CONTROL");

      gotoXY(0, 2);
      LcdP("Current volts: %d.%d", int(vol), ((int(vol * 100)) % 100) / 10);
      gotoXY(0, 3);
      LcdP("State: %s", theState);
      gotoXY(0, 4);
      LcdP("High: %d.%d Low: %d.%d ", int(myVars.highV), ((int(myVars.highV * 100)) % 100) / 10, int(myVars.lowV), ((int(myVars.lowV * 100)) % 100) / 10);
      gotoXY(0, 5);
      LcdP("On:    %05ld:%02ld:%02ld", upSecs / 3600, (upSecs % 3600) / 60, upSecs % 60);
      gotoXY(0, 6);
      LcdP("Off:   %05ld:%02ld:%02ld", downSecs / 3600, (downSecs % 3600) / 60, downSecs % 60);
      gotoXY(0, 7);
      LcdP("Total: %05ld:%02ld:%02ld", secs / 3600, (secs % 3600) / 60, secs % 60);

      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++;
    }
  }
}
HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.