leonvandenbeukel / 7-Segment-Digital-Clock-V2

A new version of a large 3D printed 7 segment digital clock with LED's
93 stars 51 forks source link

Add 5 LED / Strip - 7 Segment Digital Clock #2

Open Eka1911 opened 3 years ago

Eka1911 commented 3 years ago

Dear Leon,

I tried to make it by increasing the number of LEDs / Strips, from 3 LEDs to 5 LEDs.

But when you turn it on, many of the leds don't light up. The following sketch that I changed.

Please provide solutions and directions, so that it can be perfect.

Thank you.

include

include // Include RTC library by Makuna: https://github.com/Makuna/Rtc

include

include

include

include

include // Please read the instructions on http://arduino.esp8266.com/Arduino/versions/2.3.0/doc/filesystem.html#uploading-files-to-file-system

define countof(a) (sizeof(a) / sizeof(a[0]))

define NUM_LEDS 142 // Total of 142 LED's ////5 led per segment X 7 segment X 4 digit + 2 dot titik

define DATA_PIN D6 // Change this if you are using another type of ESP board than a WeMos D1 Mini

define MILLI_AMPS 4000 // asumsi 800mA jika 1ed per segment maka 5 ledper segment 4.000mA

define COUNTDOWN_OUTPUT D5 // COUNTDOWN_OUTPUT D5 to buzzer 3 beeps when the countdown timer is reached

define WIFIMODE 2 // 0 = Only Soft Access Point, 1 = Only connect to local WiFi network with UN/PW, 2 = Both

if defined(WIFIMODE) && (WIFIMODE == 0 || WIFIMODE == 2)

const char APssid = "7Segment-5Led";
const char
APpassword = "1234567890";

endif

if defined(WIFIMODE) && (WIFIMODE == 1 || WIFIMODE == 2)

include "Credentials.h" // Create this file in the same directory as the .ino file and add your credentials (#define SID YOURSSID and on the second line #define PW YOURPASSWORD)

const char ssid = SID; const char password = PW;

endif

RtcDS3231 Rtc(Wire); ESP8266WebServer server(80); ESP8266HTTPUpdateServer httpUpdateServer; CRGB LEDs[NUM_LEDS];

// Settings unsigned long prevTime = 0; byte r_val = 255; byte g_val = 0; byte b_val = 0; bool dotsOn = true; byte brightness = 255; float temperatureCorrection = -3.0; byte temperatureSymbol = 12; // 12=Celcius, 13=Fahrenheit check 'numbers' byte clockMode = 0; // Clock modes: 0=Clock, 1=Countdown, 2=Temperature, 3=Scoreboard unsigned long countdownMilliSeconds; unsigned long endCountDownMillis; byte hourFormat = 24; // Change this to 12 if you want default 12 hours format instead of 24
CRGB countdownColor = CRGB::Green; byte scoreboardLeft = 0; byte scoreboardRight = 0; CRGB scoreboardColorLeft = CRGB::Green; CRGB scoreboardColorRight = CRGB::Red; CRGB alternateColor = CRGB::Black; long numbers[] = { 0b00000111111111111111111111111111111, // [0] 0 ////5 led per segment 0b00000111110000000000000000000011111, // [1] 1 ////5 led per segment 0b11111111111111100000111111111100000, // [2] 2 ////5 led per segment 0b11111111111111100000000001111111111, // [3] 3 ////5 led per segment 0b11111111110000011111000000000011111, // [4] 4 ////5 led per segment 0b11111000001111111111000001111111111, // [5] 5 ////5 led per segment 0b11111000001111111111111111111111111, // [6] 6 ////5 led per segment 0b00000111111111100000000000000011111, // [7] 7 ////5 led per segment 0b11111111111111111111111111111111111, // [8] 8 ////5 led per segment 0b11111111111111111111000001111111111, // [9] 9 ////5 led per segment 0b00000000000000000000000000000000000, // [10] off ////5 led per segment 0b11111111111111111111000000000000000, // [11] degrees symbol ////5 led per segment 0b00000000001111111111111111111100000, // [12] C(elsius) ////5 led per segment 0b11111000001111111111111110000000000, // [13] F(ahrenheit) ////5 led per segment };

void setup() { pinMode(COUNTDOWN_OUTPUT, OUTPUT); Serial.begin(115200); delay(200);

// RTC DS3231 Setup Rtc.Begin();
RtcDateTime compiled = RtcDateTime(DATE, TIME);

if (!Rtc.IsDateTimeValid()) { if (Rtc.LastError() != 0) { // we have a communications error see https://www.arduino.cc/en/Reference/WireEndTransmission for what the number means Serial.print("RTC communications error = "); Serial.println(Rtc.LastError()); } else { // Common Causes: // 1) first time you ran and the device wasn't running yet // 2) the battery on the device is low or even missing Serial.println("RTC lost confidence in the DateTime!"); // following line sets the RTC to the date & time this sketch was compiled // it will also reset the valid flag internally unless the Rtc device is // having an issue Rtc.SetDateTime(compiled); } }

WiFi.setSleepMode(WIFI_NONE_SLEEP);

delay(200); //Serial.setDebugOutput(true);

FastLED.addLeds<WS2812B, DATA_PIN, GRB>(LEDs, NUM_LEDS);
FastLED.setDither(false); FastLED.setCorrection(TypicalLEDStrip); FastLED.setMaxPowerInVoltsAndMilliamps(5, MILLI_AMPS); fill_solid(LEDs, NUM_LEDS, CRGB::Black); FastLED.show();

// WiFi - AP Mode or both

if defined(WIFIMODE) && (WIFIMODE == 0 || WIFIMODE == 2)

WiFi.mode(WIFI_AP_STA); WiFi.softAP(APssid, APpassword); // IP is usually 192.168.4.1 Serial.println(); Serial.print("SoftAP IP: "); Serial.println(WiFi.softAPIP());

endif

// WiFi - Local network Mode or both

if defined(WIFIMODE) && (WIFIMODE == 1 || WIFIMODE == 2)

byte count = 0; WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { // Stop if cannot connect if (count >= 60) { Serial.println("Could not connect to local WiFi.");
return; }

delay(500);
Serial.print(".");
LEDs[count] = CRGB::Green;
FastLED.show();
count++;

} Serial.print("Local IP: "); Serial.println(WiFi.localIP());

IPAddress ip = WiFi.localIP(); Serial.println(ip[3]);

endif

httpUpdateServer.setup(&server);

// Handlers server.on("/color", HTTP_POST, []() {
r_val = server.arg("r").toInt(); g_val = server.arg("g").toInt(); b_val = server.arg("b").toInt(); server.send(200, "text/json", "{\"result\":\"ok\"}"); });

server.on("/setdate", HTTP_POST, []() { // Sample input: date = "Dec 06 2009", time = "12:34:56" // Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec String datearg = server.arg("date"); String timearg = server.arg("time"); Serial.println(datearg); Serial.println(timearg);
char d[12]; char t[9]; datearg.toCharArray(d, 12); timearg.toCharArray(t, 9); RtcDateTime compiled = RtcDateTime(d, t); Rtc.SetDateTime(compiled);
clockMode = 0;
server.send(200, "text/json", "{\"result\":\"ok\"}"); });

server.on("/brightness", HTTP_POST, []() {
brightness = server.arg("brightness").toInt();
server.send(200, "text/json", "{\"result\":\"ok\"}"); });

server.on("/countdown", HTTP_POST, []() {
countdownMilliSeconds = server.arg("ms").toInt();
byte cd_r_val = server.arg("r").toInt(); byte cd_g_val = server.arg("g").toInt(); byte cd_b_val = server.arg("b").toInt(); digitalWrite(COUNTDOWN_OUTPUT, LOW); countdownColor = CRGB(cd_r_val, cd_g_val, cd_b_val); endCountDownMillis = millis() + countdownMilliSeconds; allBlank(); clockMode = 1;
server.send(200, "text/json", "{\"result\":\"ok\"}"); });

server.on("/temperature", HTTP_POST, []() {
temperatureCorrection = server.arg("correction").toInt(); temperatureSymbol = server.arg("symbol").toInt(); clockMode = 2;
server.send(200, "text/json", "{\"result\":\"ok\"}"); });

server.on("/scoreboard", HTTP_POST, []() {
scoreboardLeft = server.arg("left").toInt(); scoreboardRight = server.arg("right").toInt(); scoreboardColorLeft = CRGB(server.arg("rl").toInt(),server.arg("gl").toInt(),server.arg("bl").toInt()); scoreboardColorRight = CRGB(server.arg("rr").toInt(),server.arg("gr").toInt(),server.arg("br").toInt()); clockMode = 3;
server.send(200, "text/json", "{\"result\":\"ok\"}"); });

server.on("/hourformat", HTTP_POST, []() {
hourFormat = server.arg("hourformat").toInt(); clockMode = 0;
server.send(200, "text/json", "{\"result\":\"ok\"}"); });

server.on("/clock", HTTP_POST, []() {
clockMode = 0;
server.send(200, "text/json", "{\"result\":\"ok\"}"); });

// Before uploading the files with the "ESP8266 Sketch Data Upload" tool, zip the files with the command "gzip -r ./data/" (on Windows I do this with a Git Bash) // *.gz files are automatically unpacked and served from your ESP (so you don't need to create a handler for each file). server.serveStatic("/", SPIFFS, "/", "max-age=86400"); server.begin();

SPIFFS.begin(); Serial.println("SPIFFS contents:"); Dir dir = SPIFFS.openDir("/"); while (dir.next()) { String fileName = dir.fileName(); size_t fileSize = dir.fileSize(); Serial.printf("FS File: %s, size: %s\n", fileName.c_str(), String(fileSize).c_str()); } Serial.println();

digitalWrite(COUNTDOWN_OUTPUT, LOW); }

void loop(){

server.handleClient();

unsigned long currentMillis = millis();
if (currentMillis - prevTime >= 1000) { prevTime = currentMillis;

if (clockMode == 0) {
  updateClock();
} else if (clockMode == 1) {
  updateCountdown();
} else if (clockMode == 2) {
  updateTemperature();      
} else if (clockMode == 3) {
  updateScoreboard();            
}

FastLED.setBrightness(brightness);
FastLED.show();

}
}

void displayNumber(byte number, byte segment, CRGB color) { /*

void allBlank() { for (int i=0; i<NUM_LEDS; i++) { LEDs[i] = CRGB::Black; } FastLED.show(); }

void updateClock() {
RtcDateTime now = Rtc.GetDateTime(); // printDateTime(now);

int hour = now.Hour(); int mins = now.Minute(); int secs = now.Second();

if (hourFormat == 12 && hour > 12) hour = hour - 12;

byte h1 = hour / 10; byte h2 = hour % 10; byte m1 = mins / 10; byte m2 = mins % 10;
byte s1 = secs / 10; byte s2 = secs % 10;

CRGB color = CRGB(r_val, g_val, b_val);

if (h1 > 0) displayNumber(h1,3,color); else displayNumber(10,3,color); // Blank

displayNumber(h2,2,color); displayNumber(m1,1,color); displayNumber(m2,0,color);

displayDots(color);
}

void updateCountdown() {

if (countdownMilliSeconds == 0 && endCountDownMillis == 0) return;

unsigned long restMillis = endCountDownMillis - millis(); unsigned long hours = ((restMillis / 1000) / 60) / 60; unsigned long minutes = (restMillis / 1000) / 60; unsigned long seconds = restMillis / 1000; int remSeconds = seconds - (minutes 60); int remMinutes = minutes - (hours 60);

Serial.print(restMillis); Serial.print(" "); Serial.print(hours); Serial.print(" "); Serial.print(minutes); Serial.print(" "); Serial.print(seconds); Serial.print(" | "); Serial.print(remMinutes); Serial.print(" "); Serial.println(remSeconds);

byte h1 = hours / 10; byte h2 = hours % 10; byte m1 = remMinutes / 10; byte m2 = remMinutes % 10;
byte s1 = remSeconds / 10; byte s2 = remSeconds % 10;

CRGB color = countdownColor; if (restMillis <= 60000) { color = CRGB::Red; }

if (hours > 0) { // hh:mm displayNumber(h1,3,color); displayNumber(h2,2,color); displayNumber(m1,1,color); displayNumber(m2,0,color);
} else { // mm:ss
displayNumber(m1,3,color); displayNumber(m2,2,color); displayNumber(s1,1,color); displayNumber(s2,0,color);
}

displayDots(color);

if (hours <= 0 && remMinutes <= 0 && remSeconds <= 0) { Serial.println("Countdown timer ended."); //endCountdown(); countdownMilliSeconds = 0; endCountDownMillis = 0; digitalWrite(COUNTDOWN_OUTPUT, HIGH); delay(500); digitalWrite(COUNTDOWN_OUTPUT,LOW); delay(500); digitalWrite(COUNTDOWN_OUTPUT, HIGH); delay(500); digitalWrite(COUNTDOWN_OUTPUT,LOW); delay(500); digitalWrite(COUNTDOWN_OUTPUT, HIGH); delay(500); digitalWrite(COUNTDOWN_OUTPUT,LOW); delay(500); return; }
}

void endCountdown() { allBlank(); for (int i=0; i<NUM_LEDS; i++) { if (i>0) LEDs[i-1] = CRGB::Black;

LEDs[i] = CRGB::Red;
FastLED.show();
delay(25);

}
}

void displayDots(CRGB color) { if (dotsOn) { LEDs[70] = color; //// value dot 5 segment per led LEDs[71] = color; //// value dot 5 segment per led } else { LEDs[70] = CRGB::Black; //// value dot 5 segment per led LEDs[71] = CRGB::Black; //// value dot 5 segment per led }

dotsOn = !dotsOn;
}

void hideDots() { LEDs[70] = CRGB::Black; //// value dot 5 segment per led LEDs[71] = CRGB::Black; //// value dot 5 segment per led }

void updateTemperature() { RtcTemperature temp = Rtc.GetTemperature(); float ftemp = temp.AsFloatDegC(); float ctemp = ftemp + temperatureCorrection; Serial.print("Sensor temp: "); Serial.print(ftemp); Serial.print(" Corrected: "); Serial.println(ctemp);

if (temperatureSymbol == 13) ctemp = (ctemp * 1.8000) + 32;

byte t1 = int(ctemp) / 10; byte t2 = int(ctemp) % 10; CRGB color = CRGB(r_val, g_val, b_val); displayNumber(t1,3,color); displayNumber(t2,2,color); displayNumber(11,1,color); displayNumber(temperatureSymbol,0,color); hideDots(); }

void updateScoreboard() { byte sl1 = scoreboardLeft / 10; byte sl2 = scoreboardLeft % 10; byte sr1 = scoreboardRight / 10; byte sr2 = scoreboardRight % 10;

displayNumber(sl1,3,scoreboardColorLeft); displayNumber(sl2,2,scoreboardColorLeft); displayNumber(sr1,1,scoreboardColorRight); displayNumber(sr2,0,scoreboardColorRight); hideDots(); }

void printDateTime(const RtcDateTime& dt) { char datestring[20];

snprintf_P(datestring, 
        countof(datestring),
        PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
        dt.Month(),
        dt.Day(),
        dt.Year(),
        dt.Hour(),
        dt.Minute(),
        dt.Second() );
Serial.println(datestring);

}

helpquick commented 3 years ago

numbers is an array of type "long", which is limited to 32 bits, and with 5 LEDs, you'd need 35 bits.

try change to "long long"

niall-cooley commented 3 years ago

@helpquick

numbers is an array of type "long", which is limited to 32 bits, and with 5 LEDs, you'd need 35 bits.

try change to "long long"

What if you want to reduct to 2 leds?

helpquick commented 3 years ago

@niall-cooley

What if you want to reduct to 2 leds?

There's a number of places in the code which need to be updated if you change the number of LEDs, e.g. start indexes will all need to be updated, the pattern in the numbers[] array need to be updated, etc.

niall-cooley commented 3 years ago

@helpquick Yeah I was thinking that Ive updated the number of LED's and the indexes. Just trying to work out the pattern in numbers[] now :)

helpquick commented 3 years ago
  _8 _9
_7     10
_6     11
  13 12
_5     _0
_4     _1
  _3 _2

your bit patterns would like like the above. Bit 0 - 1st LED, bit 13 = last LED

So, if yo put a 1 next to the LEDs which would be lit for a given number, you can put a 1 in the bit lit, and 0 in the unlit bit.

I worked backwards from bit 13, so you can type it in order. for a 1, it is 0 0 1 1 0 0 0 0 0 0 0 0 1 1

A quick go at it is below:

0b00111111111111  // 0
0b00110000000011  // 1
0b11111100111100  // 2
0b11111100001111  // 3
0b11110011000011  // 4
0b11001111001111  // 5
0b11001111111111  // 6
0b00111100000011  // 7
0b11111111111111  // 8
0b11111111001111  // 9

Or something like that.

niall-cooley commented 3 years ago

@helpquick Thank you!

MacGyverr commented 3 years ago

So what if you wanted to use 7 LEDs per segment, my issue is that long long won't work and you can't use double because of this. for (byte i=0; i<49; i++){ yield(); LEDs[i + startindex] = ((numbers[number] & 1 << i) == 1 << i) ? color : alternateColor; } long long numbers[] = { 0b0000000111111111111111111111111111111111111111111, //0 0b1111111000000000000000000000000000011111110000000, //1 0b0000000111111111111110000000111111111111111111111, //2 0b1111111111111100000000000000111111111111111111111, //3 0b1111111000000000000001111111000000011111111111111, //4 0b1111111111111100000001111111111111100000001111111, //5 0b1111111111111111111111111111111111100000001111111, //6 0b1111111000000000000000000000111111111111110000000, //7 0b1111111111111111111111111111111111111111111111111, //8 0b1111111000000000000001111111111111111111111111111, //9 0b0000000000000000000000000000000000000000000000000, //10 off 0b0000000000000000000001111111111111111111111111111, //degrees symbol 0b0000000111111111111111111111111111100000000000000, //C 0b0000000000000011111111111111111111100000001111111, //F }; Any ideas?

niall-cooley commented 3 years ago

Hi @MacGyverr im not 100% sure I would need to try it… You could possibly use a float?

helpquick commented 3 years ago

@MacGyverr @niall-cooley You can't use float or double as the numbers array essentially holds a bitmap of which LEDs should be lit or not for each digit.

Why will long long not work? it should be 64 bits, which is enough to hold the required 49-bit bitmap? (I haven't actually tried more than 3 LEDs per segment).

niall-cooley commented 3 years ago

@MacGyverr @niall-cooley You can't use float or double as the numbers array essentially holds a bitmap of which LEDs should be lit or not for each digit.

Why will long long not work? it should be 64 bits, which is enough to hold the required 49-bit bitmap? (I haven't actually tried more than 3 LEDs per segment).

@helpquick your completely right I didn’t think on it like that! Just an update with help from your comments above I got 2 per segment working perfectly 😊 ![Uploading 0206B1BD-874F-4C83-B346-B5FC2F80FC94.jpeg…]()

MacGyverr commented 3 years ago

It starts to do weird things above 32 per segment, like never turning off certain lights and turning on less than the number it should, and if I use long long, the 32nd or 33rd light stays out all down the strip and it doesn't work right. This array method looks promising: byte digits[12][28] = {{0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, // Digit 0 {0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1}, // Digit 1 {1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0}, // Digit 2 {1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1}, // Digit 3 {1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1}, // Digit 4 {1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1}, // Digit 5 {1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, // Digit 6 {0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1}, // Digit 7 {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, // Digit 8 {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1}, // Digit 9 | 2D Array for numbers on 7 segment {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0}, // Digit *0 {0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0}}; // Digit C and then for(int k=0; k<=27;k++){ // Serial.print(digits[digit][k]); if (digits[digit][k]== 1){leds[cursor]=ledColor;} else if (digits[digit][k]==0){leds[cursor]=0x000000;}; cursor ++; }; But my complete lack of programming skills and my inability to fix a simple size issue, makes me think I'll never be able to convert Leon's superior web-server based code into using that either. :)

helpquick commented 3 years ago

Alternative option would be to have the numbers[] array be just 1 bit per segment, then add an extra for loop to expand it to the number of LEDs

// bitmap for LEDs 
byte numbers[] = { 
//  7654321
  0b0111111,  // [0] 0
  0b0100001,  // [1] 1
  0b1110110,  // [2] 2
  0b1110011,  // [3] 3
  0b1101001,  // [4] 4
  0b1011011,  // [5] 5
  0b1011111,  // [6] 6
  0b0110001,  // [7] 7
  0b1111111,  // [8] 8
  0b1111011,  // [9] 9
  0b0000000,  // [10] off
  0b1111000,  // [11] degrees symbol
  0b0011110,  // [12] C(elsius)
  0b1011100,  // [13] F(ahrenheit)
};

then: in the displayNumber function:

  for (byte i=0; i<7; i++){                // 7 segments
    for (byte j=0; j<7; j++ {              // 7 LEDs per segment
      yield();
      LEDs[i * 7 + j + startindex] = ((numbers[number] & 1 << i) == 1 << i) ? color : alternateColor;
    }
  } 

This is just an idea, I've not tested it. [edited] need to multiply i by the number of LEDs per segment

MacGyverr commented 3 years ago

That worked perfectly, I don't know why I didn't think to do that in the first place, it is so simple but so clever and best I can tell, doesn't cause any extra flickering or anything. Thank you so much, you saved me soooooo much unnecessary work. :)

helpquick commented 3 years ago

In case anyone is interested, I have uploaded an improved version of the code here: 7-Segment-WiFi-Clock

I am still working on improvements, but as it stands, number of LEDs per segment can be configured, WiFi uses AutoConnect captive portal, so no need for hard-coded SSID / PSK, and time will update from NTP.