Skip to content

Instantly share code, notes, and snippets.

@RedL0tus
Last active January 8, 2020 12:57
Show Gist options
  • Save RedL0tus/b521a65cb32255e23a07fcf0d6cd425c to your computer and use it in GitHub Desktop.
Save RedL0tus/b521a65cb32255e23a07fcf0d6cd425c to your computer and use it in GitHub Desktop.
Code for Arduinix
/*
* This code is intended to use with Arduinix Shield
*
* 2019-12-22: Basic functionalities by Kay Lin
*
* This is free and unencumbered software released into the public domain.
*
* Anyone is free to copy, modify, publish, use, compile, sell, or
* distribute this software, either in source code form or as a compiled
* binary, for any purpose, commercial or non-commercial, and by any
* means.
*
* In jurisdictions that recognize copyright laws, the author or authors
* of this software dedicate any and all copyright interest in the
* software to the public domain. We make this dedication for the benefit
* of the public at large and to the detriment of our heirs and
* successors. We intend this dedication to be an overt act of
* relinquishment in perpetuity of all present and future rights to this
* software under copyright law.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* For more information, please refer to <http://unlicense.org/>
*/
// define NUM_TUBES 6
#define DELAY_TIME 5
#define TUBES_PER_SET 2
#define NUM_SETS 3
#define BINARY_LENGTH 4
#define CATHODE_START_PIN 2
#define ANODE_END_PIN 13
#include <Wire.h>
#include <RTClib.h>
RTC_DS3231 rtc;
// Abstraction for each tube set
class TubeSet {
private:
byte previousNumbers[TUBES_PER_SET];
byte currentNumbers[TUBES_PER_SET];
PinStatus previousStates[TUBES_PER_SET * BINARY_LENGTH];
PinStatus currentStates[TUBES_PER_SET * BINARY_LENGTH];
public:
TubeSet() {
memset(this->previousNumbers, 0, sizeof(this->previousNumbers));
memset(this->currentNumbers, 0, sizeof(this->currentNumbers));
memset(this->previousStates, LOW, sizeof(this->previousStates));
memset(this->currentStates, LOW, sizeof(this->previousStates));
}
void setNumber(byte index, byte number) {
// Update previous states
memcpy(this->previousNumbers, this->currentNumbers, sizeof(this->previousNumbers));
memcpy(this->previousStates, this->currentStates, sizeof(this->previousStates));
// Update current states
byte numbers[TUBES_PER_SET];
memcpy(numbers, this->currentNumbers, sizeof(numbers));
numbers[index] = number;
memcpy(this->currentNumbers, numbers, sizeof(this->currentNumbers));
PinStatus states[TUBES_PER_SET * BINARY_LENGTH];
for (byte i = 0; i < TUBES_PER_SET; i++) {
for (byte j = 0; j < BINARY_LENGTH; j++) {
states[(i * BINARY_LENGTH) + j] = (PinStatus) bitRead(numbers[i], j);
}
}
memcpy(this->currentStates, states, sizeof(this->currentStates));
}
void setPreviousStates() {
for (byte i = 0; i < (TUBES_PER_SET * BINARY_LENGTH); i++) {
digitalWrite(CATHODE_START_PIN + i, this->previousStates[i]);
}
}
void setCurrentStates() {
for (byte i = 0; i < (TUBES_PER_SET * BINARY_LENGTH); i++) {
digitalWrite(CATHODE_START_PIN + i, this->currentStates[i]);
}
}
};
class Tubes {
private:
TubeSet tubeSets[NUM_SETS];
public:
Tubes() {
for (byte i = 0; i < NUM_SETS; i++) {
tubeSets[i] = TubeSet();
}
}
void setNumber(byte index, byte number) {
byte indexOfSet = index % NUM_SETS;
byte indexInsideSet = index / NUM_SETS;
this->tubeSets[indexOfSet].setNumber(indexInsideSet, number);
}
static void setAnode(byte index) {
for (byte i = 0; i < NUM_SETS; i++) {
if (index == i) {
digitalWrite(ANODE_END_PIN - i, HIGH);
} else {
digitalWrite(ANODE_END_PIN - i, LOW);
}
}
}
void displayNumbers() {
for (byte i = 0; i < NUM_SETS; i++) {
this->setAnode(i);
this->tubeSets[i].setCurrentStates();
delay(DELAY_TIME);
}
}
};
Tubes tubes;
void setup() {
Serial.begin(9600);
// Rua!
for (byte i = 0; i < (NUM_SETS * TUBES_PER_SET * BINARY_LENGTH); i++) {
pinMode(CATHODE_START_PIN + i, OUTPUT);
}
for (byte i = 0; i < NUM_SETS; i++) {
pinMode(ANODE_END_PIN - i, OUTPUT);
}
// Initialize I2C
Wire.begin();
// Initialize RTC
if (rtc.begin()) {
if (rtc.lostPower()) {
Serial.println("RTC lost power, lets set the time!");
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
} else {
Serial.println("No RTC found!");
}
}
//int count = 0;
//int num = 0;
void loop() {
// count += 1;
// if (count == 50) {
// count = 0;
// num += 1;
// }
// for (byte i = 0; i < (NUM_SETS * TUBES_PER_SET); i++) {
// tubes.setNumber(i, (num + i) % 10);
// }
DateTime timeNow = rtc.now();
byte hourNum = timeNow.hour();
byte minuteNum = timeNow.minute();
byte secondNum = timeNow.second();
// Fill in the Number array used to display on the tubes.
tubes.setNumber(0, hourNum / 10);
tubes.setNumber(1, hourNum % 10);
tubes.setNumber(2, minuteNum / 10);
tubes.setNumber(3, minuteNum % 10);
tubes.setNumber(4, secondNum / 10);
tubes.setNumber(5, secondNum % 10);
tubes.displayNumbers();
}
/*
* This code is intended to use with Arduinix Shield
*
* 2020-01-01: Blynk blynk
* 2019-12-22: Basic functionalities by Kay Lin
*
* This is free and unencumbered software released into the public domain.
*
* Anyone is free to copy, modify, publish, use, compile, sell, or
* distribute this software, either in source code form or as a compiled
* binary, for any purpose, commercial or non-commercial, and by any
* means.
*
* In jurisdictions that recognize copyright laws, the author or authors
* of this software dedicate any and all copyright interest in the
* software to the public domain. We make this dedication for the benefit
* of the public at large and to the detriment of our heirs and
* successors. We intend this dedication to be an overt act of
* relinquishment in perpetuity of all present and future rights to this
* software under copyright law.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* For more information, please refer to <http://unlicense.org/>
*/
// define NUM_TUBES 6
#define DELAY_TIME 5
#define TUBES_PER_SET 2
#define NUM_SETS 3
#define BINARY_LENGTH 4
#define CATHODE_START_PIN 2
#define ANODE_END_PIN 13
#define BLYNK_MSG_LIMIT 0
#define WIFI_SSID "Debiantai"
#define WIFI_PW "SuperDebiantai"
#define BLYNK_TOKEN "oooooooooooooooooooooooooooof"
#define BLYNK_PRINT Serial
#include <SPI.h>
#include <Wire.h>
#include <RTClib.h> // Needs RTClib from Adafruit
#include <TimeLib.h>
#include <WiFiUdp.h>
#include <WiFiNINA.h> // WiFiNINA
#include <NTPClient.h> // NTPClient
#include <BlynkSimpleWiFiNINA.h> // Blynk
#include <WidgetRTC.h> // RTC Widget in Blynk
RTC_DS3231 rtc;
WidgetRTC blynkRtc;
char *ssid = WIFI_SSID;
char *password = WIFI_PW;
char *blynk_token = BLYNK_TOKEN;
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);
// Abstraction for each tube set
class TubeSet {
private:
byte previousNumbers[TUBES_PER_SET];
byte currentNumbers[TUBES_PER_SET];
PinStatus previousStates[TUBES_PER_SET * BINARY_LENGTH];
PinStatus currentStates[TUBES_PER_SET * BINARY_LENGTH];
public:
TubeSet() {
memset(this->previousNumbers, 0, sizeof(this->previousNumbers));
memset(this->currentNumbers, 0, sizeof(this->currentNumbers));
memset(this->previousStates, LOW, sizeof(this->previousStates));
memset(this->currentStates, LOW, sizeof(this->previousStates));
}
void setNumber(byte index, byte number) {
// Update previous states
memcpy(this->previousNumbers, this->currentNumbers, sizeof(this->previousNumbers));
memcpy(this->previousStates, this->currentStates, sizeof(this->previousStates));
// Update current states
byte numbers[TUBES_PER_SET];
memcpy(numbers, this->currentNumbers, sizeof(numbers));
numbers[index] = number;
memcpy(this->currentNumbers, numbers, sizeof(this->currentNumbers));
PinStatus states[TUBES_PER_SET * BINARY_LENGTH];
memcpy(states, this->currentStates, sizeof(states));
for (byte i = 0; i < BINARY_LENGTH; i++) {
states[(index * 4) + i] = (PinStatus) bitRead(numbers[index], i);
}
memcpy(this->currentStates, states, sizeof(this->currentStates));
}
void setPreviousStates() {
for (byte i = 0; i < (TUBES_PER_SET * BINARY_LENGTH); i++) {
digitalWrite(CATHODE_START_PIN + i, this->previousStates[i]);
}
}
void setCurrentStates() {
for (byte i = 0; i < (TUBES_PER_SET * BINARY_LENGTH); i++) {
digitalWrite(CATHODE_START_PIN + i, this->currentStates[i]);
}
}
byte getCurrentNumber(byte index) {
return this->currentNumbers[index];
}
};
class Tubes {
private:
TubeSet tubeSets[NUM_SETS];
public:
Tubes() {
for (byte i = 0; i < NUM_SETS; i++) {
tubeSets[i] = TubeSet();
}
}
void setNumber(byte index, byte number) {
byte indexOfSet = index % NUM_SETS;
byte indexInsideSet = index / NUM_SETS;
this->tubeSets[indexOfSet].setNumber(indexInsideSet, number);
}
byte getCurrentNumber(byte index) {
byte indexOfSet = index % NUM_SETS;
byte indexInsideSet = index / NUM_SETS;
return this->tubeSets[indexOfSet].getCurrentNumber(indexInsideSet);
}
static void setAnode(byte index) {
for (byte i = 0; i < NUM_SETS; i++) {
if (index == i) {
digitalWrite(ANODE_END_PIN - i, HIGH);
} else {
digitalWrite(ANODE_END_PIN - i, LOW);
}
}
}
void displayNumbers() {
for (byte i = 0; i < NUM_SETS; i++) {
this->setAnode(i);
this->tubeSets[i].setCurrentStates();
delay(DELAY_TIME);
}
}
};
Tubes tubes;
bool NTPInitialized = false;
bool NTPDisabled = false;
byte timeOffsetHour = -8;
bool BlynkRtc = false;
bool paused = false;
bool dimmed = false;
void setup() {
// Serial
Serial.begin(9600);
// Setup pins
for (byte i = 0; i < (NUM_SETS * TUBES_PER_SET * BINARY_LENGTH); i++) {
pinMode(CATHODE_START_PIN + i, OUTPUT);
}
for (byte i = 0; i < NUM_SETS; i++) {
pinMode(ANODE_END_PIN - i, OUTPUT);
}
// Lit up the tubes
tubes.setNumber(0, 15);
tubes.setNumber(1, 15);
tubes.setNumber(2, 15);
tubes.setNumber(3, 15);
tubes.setNumber(4, 15);
tubes.setNumber(5, 9);
tubes.displayNumbers();
// Blynk
Blynk.begin(blynk_token, ssid, password);
// Initialize I2C
Wire.begin();
// Initialize RTC
if (rtc.begin()) {
if (rtc.lostPower()) {
Serial.println("RTC lost power, resetting time");
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
} else {
Serial.println("No RTC found, switching to Blynk RTC");
Serial.println("Waiting for connection...");
while (WiFi.status() != WL_CONNECTED) {
delay(50);
}
BlynkRtc = true;
}
}
byte hourTemp = 0;
byte minuteTemp = 0;
byte secondTemp = 0;
void ntpAdjustRTC() {
if (! NTPInitialized) {
timeClient.begin();
}
timeClient.setTimeOffset(timeOffsetHour * 3600);
timeClient.update();
rtc.adjust(DateTime(timeClient.getEpochTime()));
}
void updateTime() {
byte secondNum;
DateTime timeNow;
if (! BlynkRtc) {
timeNow = rtc.now();
secondNum = timeNow.second();
} else {
secondNum = second();
}
if (secondTemp != secondNum) {
tubes.setNumber(4, secondNum / 10);
tubes.setNumber(5, secondNum % 10);
secondTemp = secondNum;
byte minuteNum;
if (! BlynkRtc) {
minuteNum = timeNow.minute();
} else {
minuteNum = minute();
}
if (minuteTemp != minuteNum) {
tubes.setNumber(2, minuteNum / 10);
tubes.setNumber(3, minuteNum % 10);
minuteTemp = minuteNum;
byte hourNum;
if (! BlynkRtc) {
hourNum = timeNow.hour();
} else {
hourNum = hour();
}
if (hourTemp != hourNum) {
tubes.setNumber(0, hourNum / 10);
tubes.setNumber(1, hourNum % 10);
hourTemp = hourNum;
// Hourly adjust
if ((WiFi.status() == WL_CONNECTED) && (! NTPDisabled)) {
ntpAdjustRTC();
}
}
}
}
}
void forceUpdateTime() {
byte hourNum;
byte minuteNum;
byte secondNum;
if (! BlynkRtc) {
DateTime timeNow = rtc.now();
hourNum = timeNow.hour();
minuteNum = timeNow.minute();
secondNum = timeNow.second();
} else {
hourNum = hour();
minuteNum = minute();
secondNum = second();
}
tubes.setNumber(0, hourNum / 10);
tubes.setNumber(1, hourNum % 10);
tubes.setNumber(2, minuteNum / 10);
tubes.setNumber(3, minuteNum % 10);
tubes.setNumber(4, secondNum / 10);
tubes.setNumber(5, secondNum % 10);
}
BLYNK_CONNECTED() {
blynkRtc.begin();
Blynk.syncAll();
Blynk.virtualWrite(9, 1);
}
BLYNK_WRITE(V0) {
if (param.asInt() == 1) {
paused = true;
} else {
paused = false;
}
}
BLYNK_WRITE(V1) {
if (param.asInt() == 1) {
timeClient.forceUpdate();
ntpAdjustRTC();
forceUpdateTime();
}
}
BLYNK_WRITE(V2) {
timeOffsetHour = param.asInt();
ntpAdjustRTC();
forceUpdateTime();
}
BLYNK_WRITE(V3) {
if (paused) {
tubes.setNumber(0, param.asInt() / 10);
tubes.setNumber(1, param.asInt() % 10);
}
}
BLYNK_WRITE(V4) {
if (paused) {
tubes.setNumber(2, param.asInt() / 10);
tubes.setNumber(3, param.asInt() % 10);
}
}
BLYNK_WRITE(V5) {
if (paused) {
tubes.setNumber(4, param.asInt() / 10);
tubes.setNumber(5, param.asInt() % 10);
}
}
BLYNK_WRITE(V6) {
if (param.asInt() == 1) {
dimmed = true;
for (byte i = 0; i < (NUM_SETS * TUBES_PER_SET); i++) {
tubes.setNumber(i, 15);
}
tubes.displayNumbers();
} else {
dimmed = false;
forceUpdateTime();
}
}
BLYNK_WRITE(V7) {
if (param.asInt() == 1) {
NTPDisabled = true;
} else {
NTPDisabled = false;
ntpAdjustRTC();
forceUpdateTime();
}
}
BLYNK_WRITE(V8) {
if (param.asInt() == 1) {
BlynkRtc = true;
NTPDisabled = true;
forceUpdateTime();
} else {
BlynkRtc = false;
NTPDisabled = false;
Blynk.virtualWrite(8, 0);
forceUpdateTime();
}
}
void loop() {
if (! dimmed) {
if ((WiFi.status() == WL_CONNECTED) && (! NTPInitialized) && (! NTPDisabled)) {
ntpAdjustRTC();
NTPInitialized = true;
}
if (! paused) {
updateTime();
}
tubes.displayNumbers();
}
Blynk.run();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment