Skip to content

Instantly share code, notes, and snippets.

@BBBSnowball
Created August 4, 2016 08:48
Show Gist options
  • Save BBBSnowball/dd97339d58b94b547604be0a83e40f50 to your computer and use it in GitHub Desktop.
Save BBBSnowball/dd97339d58b94b547604be0a83e40f50 to your computer and use it in GitHub Desktop.
ESP8266 plus 7segment display with MAX7219
/*
* Copyright (c) 2015, Majenko Technologies
* Copyright (c) 2016, Benjamin Koch
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* * Neither the name of Majenko Technologies nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// This also uses ideas (and similar code in some places) from
// https://github.com/tzapu/WiFiManager
// MIT license, (c) 2015 tzapu
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <DNSServer.h>
#include "LedControl.h"
const char ssid[] = "wifidisplay";
#define password NULL
const bool captivePortal = true;
//const char defaultText[] = "Herzlichen Glueckwunsch.";
const char defaultText[] = "HERZLICHEN GLUECKWUNSCH.";
#define IP(a,b,c,d) (IPAddress((uint32_t)((a) | ((b)<<8) | ((c)<<16) | ((d)<<24))))
IPAddress myIp = IP(192, 168, 1, 1);
ESP8266WebServer server(80);
DNSServer dnsServer;
// data on pin 14, clk on 13, cs on 12, one device
LedControl sevenseg = LedControl(14,13,12,1);
const int led = 0;
void handleRoot();
void handleNotFound();
void changeText();
void scrollText();
void setText(const char* text);
void setup() {
pinMode(led, OUTPUT);
digitalWrite(led, 0);
Serial.begin(115200);
Serial.print(F("Clear 7seg display...\t"));
sevenseg.shutdown(0,false);
sevenseg.setIntensity(0,8);
sevenseg.clearDisplay(0);
Serial.println("done");
Serial.print(F("Open AP with SSID '"));
Serial.print(ssid);
if (password) {
Serial.print(F("' and password '"));
Serial.print(password);
}
Serial.print(F("'...\t"));
WiFi.softAPConfig(myIp,
captivePortal ? myIp : IP(0, 0, 0, 0),
IP(255, 255, 255, 0));
WiFi.softAP(ssid, password);
Serial.println("done");
static WiFiEventHandler connected = WiFi.onSoftAPModeStationConnected(
[](const WiFiEventSoftAPModeStationConnected&) {
Serial.println(F("client connected"));
}
);
static WiFiEventHandler disconnected = WiFi.onSoftAPModeStationDisconnected(
[](const WiFiEventSoftAPModeStationDisconnected&) {
Serial.println(F("client disconnected"));
}
);
Serial.print(F("Starting mDNS responder...\t"));
if (MDNS.begin("display", myIp)) {
Serial.println(F("done"));
} else {
Serial.println(F("failed"));
}
if (captivePortal) {
Serial.print(F("Starting DNS server...\t"));
dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
dnsServer.start(53, "*", WiFi.softAPIP());
Serial.println(F("done"));
}
Serial.print(F("Starting HTTP server...\t"));
server.on("/", handleRoot);
server.on("/t", changeText);
server.onNotFound ( handleNotFound );
server.begin();
Serial.println(F("done"));
if (defaultText)
setText(defaultText);
}
void loop() {
dnsServer.processNextRequest();
server.handleClient();
scrollText();
}
const char mainPage[] =
"<html>"
"<head><title>WiFi display</title></head>"
"<body>"
"<h1>WiFi display</h1>"
"<form action=\"/t\" method=\"POST\">"
"<p>"
"<label for=\"t\">Text: </label>"
"<input type=\"text\" name=\"t\" id=\"t\"/>"
"</p>"
"<p>"
"<input type=\"submit\" value=\"Display text\"/>"
"</p>"
"</form>"
"</body>"
"</html>";
void handleRoot() {
Serial.println("handleRoot");
digitalWrite(led, 1);
server.send (200, "text/html", mainPage);
digitalWrite(led, 0);
}
void handleNotFound() {
Serial.print("handleNotFound: ");
Serial.println(server.uri());
if (captivePortal && server.uri() != String("/favicon.ico")) {
// redirect everything to root page because we are a captive portal
// (Chrome/Android will try /generate_204, Windows will try /fwlink)
server.sendHeader("Location", "http://192.168.1.1/", true);
server.send(302, "text/plain", "");
// stop() is needed because we send no content length
server.client().stop();
} else {
server.send(404, "text/plain", "not found");
}
}
static uint8_t sevensegBuffer[64];
static uint8_t sevensegLength = 0;
static uint8_t sevensegPos = 0;
static unsigned long long sevensegNextScrollTime = 0;
#define SEVENSEG_SCROLL_DELAY 500
#define INV 0x01
static const uint8_t sevensegAsciiToPattern[] PROGMEM = {
// space, special chars (32-47)
0x00, 0xb0, 0x22, INV, INV, INV, INV, 0x20, INV, INV, INV, INV, 0x04, 0x01, 0x80, INV,
// numbers (48-57)
0x7e, 0x30, 0x6d, 0x79, 0x33, 0x5b, 0x5f, 0x70, 0x7f, 0x7b,
// more special chars (58-63)
0x80, 0x90, INV, INV, INV, 0xe1, INV,
// upper-case letters (64-90)
0x77, 0x7f, 0x4e, 0x7e, 0x4f, 0x47, 0x5e, 0x37, 0x30, 0x3c, 0x07, 0x0e, 0, 0x15, 0x7e, // A-O
0x67, 0x73, 0x05, 0x5b, 0x0f, 0x3e, 0x1c, 0, 0x37, 0x73, 0x6d,
// special chars (91-96)
INV, INV, INV, 0x40, 0x08, 0x20,
// lower-case letters (97-122)
0x77, 0x1f, 0x0d, 0x3d, 0x4f, 0x47, 0x7b, 0x17, 0x10, 0x3c, 0x07, 0x0e, 0, 0x15, 0x1d, // A-O
0x67, 0x73, 0x05, 0x5b, 0x0f, 0x3e, 0x1c, 0, 0x37, 0x7b, 0x6d,
};
void sevensegShow() {
for (int i=0; i<8; i++) {
uint8_t x;
if ((sevensegPos+i)%256 >= sevensegLength)
x = 0;
else
x = sevensegBuffer[(sevensegPos+i)%256];
sevenseg.setRow(0, 7-i, x);
}
}
void setText(const char* text) {
sevensegPos = 0;
sevensegLength = 0;
for (; *text && sevensegLength < sizeof(sevensegBuffer)/sizeof(*sevensegBuffer); text++) {
char x = *text;
if (x < 32 || x > 127)
x = 32;
if ((x == '.') && sevensegLength > 0) {
sevensegBuffer[sevensegLength-1] |= 0x80;
} else if (sevensegLength+1<sizeof(sevensegBuffer)/sizeof(*sevensegBuffer)
&& (x == 'M' || x == 'm' || x == 'W' || x == 'w')) {
switch (x) {
case 'M':
sevensegBuffer[sevensegLength++] = 0x66;
sevensegBuffer[sevensegLength++] = 0x72;
break;
case 'm':
sevensegBuffer[sevensegLength++] = 0x15;
sevensegBuffer[sevensegLength++] = 0x15;
break;
case 'W':
sevensegBuffer[sevensegLength++] = 0x1e;
sevensegBuffer[sevensegLength++] = 0x3c;
break;
case 'w':
sevensegBuffer[sevensegLength++] = 0x1c;
sevensegBuffer[sevensegLength++] = 0x1c;
break;
}
} else {
sevensegBuffer[sevensegLength++] = pgm_read_byte_near(&sevensegAsciiToPattern[x-32]);
}
}
sevensegShow();
sevensegNextScrollTime = millis() + SEVENSEG_SCROLL_DELAY;
}
void changeText() {
if (server.hasArg("t")) {
String text = server.arg("t");
Serial.print("text: ");
Serial.println(text);
setText(text.c_str());
}
handleRoot();
}
void scrollText() {
if (sevensegLength <= 8)
return;
unsigned long long m = millis();
if (sevensegNextScrollTime - m >= 0 && sevensegNextScrollTime - m < 0x80000000)
return;
sevensegPos++;
if (sevensegPos >= sevensegLength && sevensegPos<249)
sevensegPos = 249;
sevensegShow();
sevensegNextScrollTime = m + SEVENSEG_SCROLL_DELAY;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment