Project tutorial

Arduino Live WiFi Web Scoreboard © CC BY-NC-SA

How to create a standalone Internet-connected digital scoreboard.

  • 3,230 views
  • 0 comments
  • 9 respects

Components and supplies

Ardgen mega
Arduino Mega 2560 & Genuino Mega 2560
×1
12002 04
Breadboard (generic)
×1
RGB LEDs
×3
7-Segment 8-Digit Display
×2
220 ohm resistors
×6
5v - 3.3v Level Converter
×1
Esp01
Everything ESP ESP8266 ESP-01
×1
Wooden Box
×1

Necessary tools and machines

Power Drill
Box Cutter

Apps and online services

About this project

Arduino Live WiFi Web Scoreboard

In this tutorial I am going to explain how I created a digital scoreboard with LEDs, a large dot-matrix display and numerical 8-digit totals. The device uses a WiFi chip to connect to the Internet to get status values such as totals and live counts, plus statuses such as on/off for the LEDs. The device is powered by a generic USB power adaptor. Under the hood it uses an Arduino Mega compatible board, which are readily available, economical, and communicate well with the ESP8266 chips.

The scoreboard is made from a wooden box, with hinges and a clip to allow for access to the internals when needed. Power usage is minimal, running the device 24/7 for a year with WiFi connections every minute will cost less than 1 euro.

What You Will Need

1 x Wooden box (€4.50 from a local store)

1 x Arduino Mega clone - I use the Elegoo boards personally (€11.50 from Amazon)

1 x Series of 4 dot-matrix displays (€15 from Internet - though I have now seen for €5)

1 x Sugru packet (€1.65 [1 of 8 for €13 from Amazon])

2 x 7-Segment 8-Digit LED displays (€4 for 2 - or €12 next day delivery)

1 x Collection of 220 ohm resistors (€0.25 - part of pack costing €5).

3 x RGB LEDs (€0.18 - part of pack of 50 costing €3)

1 x ESP8266 chip (€5)

1 x 5v <> 3.5v level switcher (€1.50)

1 x Matt black spray paint (€2 can from a local store).

1 x Small breadboard 'DuPont' cables and pins - (€3 - usually part of any starter pack)

Access to a drill, box cutter, USB cable Cost is therefore around €50 - although existing parts could be reused, and ordering through Aliexpress for example would bring that down to nearer €30. The project takes 4 - 8 hours to complete, including waiting for the box to dry.

Preparing the RGB LEDs

Preparing the RGB LEDs (Red-Green-Blue Light-Emitting-Diodes) is quite time consuming, but satisfying once it is completed. I am using RGB LEDs to enable me to show different colours using the 'same' LED. In reality these RGB LEDs contain 3 LEDs inside and by controlling each light you can get a mix of colour.

Each LED needs a resistor in circuit, I use 220 ohm resistors with my LEDs and have never had a problem. Other tutorials may suggest different values, use whatever resistors you want, but do remember to include them, otherwise you will burn out your LEDs.

I won't go into too much depth about LEDs as tutorials such as this one have covered the subject perfectly.

What you need to do is solder a resistor to each of the three shorter legs of your RGB LED. I like to use a 'helping hands' device such as this one. Firstly wrap the wires together, then solder the connection, wait 30 seconds or so and you should find the wires are strongly attached to each other. Eventually you will want to cover the connection with insulating tape or heat shrink tubing.

In the second picture you can see my three RGB LEDs, after I have soldered three resistors and then further wires which will go to the Arduino. Additionally I have soldered a wire to the common pin, which returns to the ground rail.

At this point you will want to connect the LEDs to your Arduino and test that they work. I would recommend following any RGB LED tutorial and cycling through the colours to check that your LEDs are performing as you wish.

In my case I noticed that the green LED was much brighter than the red LED. This seems to be fairly common. If this is a problem then you might consider a larger value resistor for the green LED.

Once you have 3 RGB LEDs that you can control and that successfully show red, green and blue, then continue to the next step.

Preparing LED Dot-matrix and 7-Segment Displays

While showing green/red LEDs is useful for displaying boolean information such as true/false, on/off, yes/no, for our scoreboard to be really interesting we are going to want to display some numbers. I am going to use two different components to show information, they are related to such a degree that we can actually wire them in sequence, and use a very minimal number of pins on our Arduino board.

The 8 digit display shown in the picture is similar to this one - these are extremely common in Arduino starter packs and while colours vary pretty much all use the MAX7219 component which allows use to set all 8 digits using just 5 pins. Without the MAX7219 component we'd need a huge number of pins and resistors to set each 7-segment digit, and that would take forever and use most if not all our Arduino pins. Another benefit of this design of 7-segment 8-digit display is that they can be daisy chained, meaning you can have up to 8 of them in sequence, again controlled by just 5 pins.

The dot-matrix display is a grid of LEDs that is in effect a low resolution display, we choose which of the dots is lit and therefore can draw simple shapes, letters and numbers. Here I'm using a special component which is four of these dot-matrix displays connected together. This gives us an 32 x 8 display, on which we can display information. This counts as 4 items in our chain of 8, so you could use two of them, or in my case mix one of these with some 7-segment displays. For this dashboard I am using two, bringing my total number of chained components to 6. All driven from 5 pins on my Arduino board, and that's including one for 5v and one for ground. Pretty efficient I think!

I will delve into the code required to drive these components, and display useful information, in a later step. For now, ensure that the components are wired to your Arduino board and perhaps follow any simple tutorial (of which there are many) to be sure that your components are working.

Preparing Our Enclosure

To look the part our device needs to sit comfortably in an enclosure, where it can live and look like a piece of consumer electronics, rather than a science fair project. Most modern day devices, such as televisions, mobile phones and Blu-ray players use plastic enclosures due to its lightweight nature and consistent form. We are going to use wood, which is heavier and more brittle, but for our needs will work perfectly well.

I have searched the Internet and endless DIY stores for the perfect project box. In the end I found the perfect project box in a 'Chinese bazaar shop' here in Spain. These are universally run by Chinese people (hence the name) and stock a huge range of products, pretty much all imported from China. They are a goldmine for useful items. Eventually I found one that stocked such a huge range of boxes that one was the perfect fit for my scoreboard project. The price was also excellent, coming in at just €4.50.

The box is made from plywood, meaning it's structurally strong but also easy to cut into using a box cutter. I tried using a jigsaw machine and a saw, but in the end found that cutting windows in these boxes is best done using a simple box cutter. My box cutter set cost just €2 from the same store.

For my project I decided to have 3 RGB LEDs, evenly spaced out at the bottom of the scoreboard. I simply used a ruler to measure out evenly spaced points, then used a power drill to create a hole. I had to experiment slightly to get the right drill piece for the LEDs, you may need to create one first and then push an LED through to see that it fits snugly. Push the drill through swiftly while holding the box down using two clamps. If you don't have access to a clamp, then a friend might do! Though I strongly recommend investing in two clamps such as these, they will easily pay for themselves over the years.

My drill was purchased for €29.99 from a typical DIY store, it's nothing too special, you really don't need anything other than a basic power drill for these projects. I imagine it will last me 20 years, so again, in the long run it's very cheap.

The dox-matrix display and 7-segment 8-digit components will need windows in your box. Again I used a basic ruler and pencil to measure out the sizes and draw where the windows would go on the box. Think carefully at this stage, you will want to ensure your displays are well spaced out, creating two windows in your box close together might cause the wood to break. I would recommend at least 15mm between windows, if not more.

Depending on the number of available pins on your Arduino, you could add several more windows or indeed several more holes for LEDs. The Arduino Mega compatible boards have plenty of pins available, if you are using an Arduino Uno compatible board then you will be far more limited.

At this point you will want to confirm which board you are using, as you should determine it's location inside the box. The reason for this is that you will need to cut a hole for the USB cable and port. I recommend using the USB port for the power as then you will be able to connect to a computer when needed to easily upload new firmware to your device. Using the barrel power socket has minimal advantages and you would still need some way to access the USB port or internal serial pins to upload new firmware. So all in all when using an Uno or Mega compatible board, I highly recommend cutting a hole for the USB port and leaving it at that.

The next stage is to paint your box. I used some cheap spray paint that I was able to buy for just €2 a can. This will last you several projects, and so the cost per project is much lower. Place your box inside a larger cardboard box and take it outside, carefully spray the box, wait 30 minutes or so, then carefully turn it over if it seems to have dried. Spray the rest of the box. You may want to wait a couple of hours for the box to dry before giving it a second layer of paint. Waiting for the box to dry is boring and so perhaps work on the software side of the project while it is drying.

Eventually you will have a very smart looking box, with correctly sized windows for your components, and a small exit on the side for the USB port of your Arduino compatible board.

Mounting Your Components

After many experiments with screws and mounting sockets, tape and glue, I discovered Sugru. I highly recommend that you save yourself many hours of messing around with drill pieces and standoffs and nails etc and just invest in some Sugru. For those who haven't yet discovered it, it's basically a 'blu-tac' style putty that you can mould in your hands, the difference being that it goes hard after a few minutes, and sticks to a huge number of surfaces, including wood and Arduino boards and components. You place a small pea sized amount on the back of your item and push it hard onto the wood, in 24 hours that connection will be solid and surprisingly tough.

Sugru isn't cheap, far from it, but when you take into account the money saved on pots of screws and standoffs, it actually represents pretty good value for money.

Take your Arduino compatible board and a packet of Sugru, spread the putty around a bit and place blobs of it on the underside of your board, then push the board firmly into place so that your USB port correctly lines up with the hole on the side of your box.

Now I wouldn't recommend fixing your components into place until you are certain that they will work. As this tutorial is split into distinct sections I am going to assume that you have returned to this part after knowing that your scoreboard correctly works.

The 8-digit displays can be mounted using some Sugru on the plastic part of the connecting wire, and possibly some Sugru through any gaps between the component and your box window. Do ensure that you have them mounted the correct way up! It is safe to handle them while your device is powered up, I would suggest you do this to ensure that you place the components correctly. You can read your display while mounting it to be 100% certain it's the right way up.

Later on in this tutorial you will see see why we are using a breadboard and two other chips. I mounted the breadboard using its sticky backing, which sticks extremely well to wood, and some tape to hold the WiFi chip to stop it moving too much.

You may wish to use hot glue to fix the wires to your breadboard, I didn't do this but have heard that it works quite well. This may be necessary if your wires don't sit very snugly in your breadboard.

Using the ESP8266 Chip

Our scoreboard needs to connect to the Internet to get data to display on our LED components. The Arduino Mega or Uno compatible boards do not contain on-board WiFi, and so we have to use a further component to connect our project to the Internet.

The ESP8266 chip is marvellous, at around $6 in the US, or 2 for 10 euros here in Europe it is extremely good value. It's actually a complete SOC (system-on-a-chip) so in many ways is effectively 'another Arduino' style component, you can upload code to the device and use it on it's own. In this project however we wish to use it alongside our Arduino compatible board, with information being passed from the ESP8266 chip to our central board.

This task is more complicated than it might otherwise be due to the fact that the ESP8266 runs at 3.3v not 5v like our Arduino Mega board. This means that signals sent from the ESP8266 can't simply be connected to digital pins on our Arduino, and we can't power the ESP8266 from the 5v pin on our Arduino.

Fortunately we have a 3.3v pin on our Arduino which is suitable for powering the ESP8266. However, we are still left with the problem of the data pins. To solve this problem we need to use a level-converter. This small chip takes in power, ground and several communication wires and outputs the same signals having been dropped down to 3.3v. This means signals from our Arduino board get converted into a voltage that the ESP8266 board can use, and vice-versa on the return.

As this setup involves several wires and multiple connections to power pins I have used a breadboard. This allows us to mount the converter chip across the divide in the breadboard, and have the top half be our '5v' zone, and the bottom half our '3.3v' zone. The power rails on each side carry 5v or 3.3v, and the ground rails themselves are connected to each other and also our Arduino.

To interface with the ESP8266 chip I use a library written by Wu Pengfei - it's two files, ESP8266.cpp and ESP8266.h. It makes connecting to the chip and making a request relatively simple.

Most of the explanation of my code comes alongside the code itself, so I won't go into great detail during this Instructable. However in effect what we do to interface with the chip is create a complete HTTP request in a single string, connect to the web server using TCP, then send our request. We then await the response, which we place into a string, and then pull out data from the string, in a format that we already understand.

My dashboard deals with an online service that I created, your dashboard will bring in data from a different source. This could be a local web server running on your home network, or a website out on the Internet, the process is the same. If you look at roughly line 490 onwards, you'll see how I'm doing it. My method is based upon examples I found on the Internet.

Wiring Up the Device

The adjoining diagram shows how I wire up my scoreboard. Please note: I haven't attempted to create a perfectly reproducible dialog, this is to give you a rough overview of what I'm doing.

You'll see that the wiring to the ESP8266 chip goes via the level converter which is attached to the breadboard.

Several wires are needed for each LED, as they are RGB LEDs, meaning we need a wire for R(red), G(green) and B(blue), additional we have a wire back to the ground rail.

Thankfully we can put the dot-matrix displays and 7-segment number components in series, meaning only one set of wires is needed.

Additionally you'll see that 5v and 3.3v power wires go both to the breadboard and via the converter.

I had considered moving from a breadboard to a more permanent solution like a protoboard, but in the end, this solutions works fine for this project. If you have a good quality breadboard, and (more importantly) good quality 'Dupont' jumper wires, then you can go a long way.

I've read that some people like to cover the breadboard and wires in hot glue, which will hold it fixed. Dried hot glue isn't a good conductor, so the circuit should be unaffected. I haven't tried this yet but it's certainly something to consider, additionally more Sugru could be used to hold jumper wires in place.

Software

Critical to the working of the scoreboard is of course the software running on the microprocessor chip. This needs to control the ESP8266 chip and request updated data from the Internet, then interpret that data and show it visually on our LED displays.

I have included full source code for my scoreboard, you will need to enter your own WiFi credentials (obviously!) and point it to a different web page. I won't go into too much detail about the web server side of things, as this is extremely flexible, and you would need to create the relevant solution for your problem.

My page returns a very simple string, in the form: |$|1|1|0|51|36|2|2|1|

I first look for the '|$|' pattern, if I see that then I can assume I've got the correct beginning of my string. Afterwards I move through the string, placing what I find into new strings, which then get turned into integer numbers. When I find a '|' character, I know I'm onto the next one. You can see exactly how I do this in the code.

Next we have to update local variables which are being held in our dashboard. You will need a variable for each piece of information that you wish to show. In my case this is 'server 1 okay', 'server 2 okay', 'cron task done', 'current users', 'current systems', 'daily users', 'daily systems'.

After re-fetching the page every 60 seconds, and converting the string returned from the web server into a series of numbers, I then update my dashboard with the new numbers.

For the server statuses, and cron status, this is simply a case of setting the colour of one of the LEDs. For me green means success, red means fail, and blue means there was an issue with the connection.

The two 7-segment 8-digit displays are both split into 4-digit segments, I then output my 4 pieces of data onto them. This is slightly more complicated than it looks, as I need to convert an integer number, to the actual digit for the 100s column, 10s column and digit column of the display. As you set each digit separately, this is necessary. You can see in the code how I approached this problem.

The central dot-matrix display shows information that changes every 2 seconds. I have a function called displayMatrix which sets one of the 4 displays to show an 'image' that I have stored in the IMAGES[] array. This array is interesting as it was generated by a web page rather than myself.

I highly recommend using the LED Matrix Editor, this page makes it very easy to create 'images' in the 8x8 format that will display correctly on your dot-matrix components. Using the page is extremely straightforward, if you click the link you will get the editor with the images from my dashboard already loaded. As you can see it's simply a case of clicking the dots to change whether they are on or off. You can easily add more images, or move the order by dragging and dropping. After making your changes, bookmark the page to be able to return to them.

In the top corner of the editor you will see the definition in code for your images. Copy and paste this into your Arduino 'sketch'. Look at the code for the displayMatrix function to see how it loops through the values to set the relevant LED in the component.

This pretty much sums up the code for my dashboard, further explanation is available in the source code itself. If you have any questions at all please ask them in the comments.

Further Ideas and Project Summary

I hope you have enjoyed reading through this tutorial, and that you are able to successfully build your own Arduino WiFi scoreboard. I think what is great about this particular project is that it could be customised in so many ways. I had a need to report live user data from a web service, your needs are likely to be very different.

Some ideas for scoreboards that I have had include:

A sports scoreboard showing current scores, perhaps useful for a school sports tournament.

A live Bitcoin price monitor, including traditional currency conversion rates as well, and perhaps stock market values.

A live weather scoreboard, showing temperatures in various cities, perhaps cycling through dozens of cities displaying one per second.

A live YouTube subscriber count.

Real sports scores pulled from a live sports site such as Livescores.com or something like ESPN.

The possibilities are endless. I wish you best of luck with your projects. Please leave any comments below.

Schematics

Wiring for Arduino Live Web Score
Wiring overview for Arduino board and components
Glidedashboard bb fzes3pynvt

Code

Arduino Live WiFi Scoreboard codeArduino
Firmware code that should be uploaded to the Arduino board
// Arduino Wifi Dashboard
// Written by Oli Norwell

// Features
// - Connects via Wifi to a remote server to retrieve status information about a web service (in this case www.whatsglide.com)
// - 3 RGB LED lights
// - 32 x 8 LED Matrix display for showing key information
// - 2 x 7Segement 8Digit LED displays for showing lesser information
// - USB powered, uses 0.3-0.4A at 5V
// - Black wooden custom built box mountable on the wall

// Budget
// Arduino Mega clone (11 euros)
// 20+ DuPont wires (2 euros)
// ESP8266 chip (4.5 euros)
// 5V->3.5V convertor (2 euros)
// Box (4.50 euros)
// 3 RGB LEDS (1 euro)
// 2 8Digit displays (10 euros)
// Matrix display (14 euros)
// Breadboard (3 euros)
// total: 50 euros

// Example feedback from server:   "|$|1|1|0|51|36|2|2|1|"

// Library includes
#include "ESP8266.h"                      // For interfacing with the ESP8266 chip
#include "LedControl.h"                   // LED Control library (http://playground.arduino.cc/Main/LedControl)

// Globals
LedControl display = LedControl(A2,A1,A0,6);  // Our daisy-chained 2 LED displays
SoftwareSerial mySerial(10, 11);              // SoftwareSerial pins for MEGA/Uno. For other boards see: https://www.arduino.cc/en/Reference/SoftwareSerial
ESP8266 wifi(mySerial);                       // Wifi connection data

// Wifi Network Details (we should really load these from an SD card)
const char *SSID     = "PUT WIFI NETWORK HERE";
const char *PASSWORD = "PUT WIFI PASSWORD HERE";

// Defines
#define REFRESH_TIME        60000     // 1 minute
#define INFO_REFRESH_TIME   2000      // 2 seconds

#define SERVER_IP   "192.168.1.112" // could just as easily be an Internet site
#define SERVER_PORT 80

#define STATUS_LIGHTS
#define DIGITAL_LEDS
//#define SERIAL_DEBUG

// Globals
int gLED_INTENSITY = 15; //15;
int gLED_COLORINT = 150; //255;     // Lower than 150 doesn't seem to work

// LED pins (each light is actually 3 LEDS, allowing us to mix and create any colour)
int red1Pin = A5;
int green1Pin = A4;
int blue1Pin = A3;

int red2Pin = 7;
int green2Pin = 6;
int blue2Pin = 5;

int red3Pin = 4;
int green3Pin = 3;
int blue3Pin = 2;

// Globals to know when we enter / are in night mode   (in night mode we show minimal LEDs - just enough to confirm that all is ok)
int g_oldnightmode = 0;
int g_nightmode = 0;

// Dot-matrix image definitions (0-9 and some icons)    - from https://xantorohara.github.io/led-matrix-editor/
const uint64_t IMAGES[] = {
  0x3c66666e76663c00,
  0x7e1818181c181800,
  0x7e060c3060663c00,
  0x3c66603860663c00,
  0x30307e3234383000,
  0x3c6660603e067e00,
  0x3c66663e06663c00,
  0x1818183030667e00,
  0x3c66663c66663c00,
  0x3c66607c66663c00,
  0xdf51515d4151515f,
  0x20aeaea2beaeaea0,
  0xdd484848484848dc,
  0x22b7b7b7b7b7b723,
  0x390a0a0a3a0a0a39,
  0xc6f5f5f5c5f5f5c6,
  0x49db490077515157,
  0x49db490073555553,
  0x49db49e08fe121ef,
  0x49db49e08fe929e7,
  0x0000b8a8a8a8b800,
  0x0000140211121400
};
const int IMAGES_LEN = sizeof(IMAGES)/8;


// Our bits of data from the remote server - all pre-set to -1, which means no data
int state_s1 = -1;
int state_s2 = -1;
int state_cj = -1;
int state_acu = -1;
int state_acs = -1;
int state_dacu = -1;
int state_dacs = -1;

bool csuccess = false;
bool success = false;
unsigned long millis_last_page_grab;
unsigned long millis_last_view_change;

// Different views
#define VIEW__CURRENT_USERS     0
#define VIEW__DAILY_USERS       1
#define VIEW__CURRENT_SYSTEMS   2
#define VIEW__DAILY_SYSTEMS     3

// Current view
int g_currentView = VIEW__CURRENT_USERS;

//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
void setup(void)
{

  #ifdef SERIAL_DEBUG     // Start Serial Monitor (but only if we are compiling with serial debugging)
  Serial.begin(9600);
  Serial.println("Glide Dash 001 - Serial Debuging Enabled");
  #endif
  
  #ifdef STATUS_LIGHTS  // Setup status lights (but only if they are activated in this build)
  pinMode(red1Pin, OUTPUT);
  pinMode(green1Pin, OUTPUT);
  pinMode(blue1Pin, OUTPUT); 
  
  pinMode(red2Pin, OUTPUT);
  pinMode(green2Pin, OUTPUT);
  pinMode(blue2Pin, OUTPUT); 
  
  pinMode(red3Pin, OUTPUT);
  pinMode(green3Pin, OUTPUT);
  pinMode(blue3Pin, OUTPUT); 
  #endif

  // Setup our LED display components
  for(int a = 0; a < display.getDeviceCount(); a++)
  {
    display.clearDisplay(a);
    display.shutdown(a, false);
    display.setIntensity(a, gLED_INTENSITY);
  }

  // Place some test data
  display.setChar(4,0,'0',false);
  display.setChar(4,1,'1',false);
  display.setChar(4,2,'2',false);
  display.setChar(4,3,'3',false);
  display.setChar(4,4,'4',false);
  display.setChar(4,5,'5',false);
  display.setChar(4,6,'6',false);
  display.setChar(4,7,'7',false);

  display.setChar(5,0,'0',false);
  display.setChar(5,1,'1',false);
  display.setChar(5,2,'2',false);
  display.setChar(5,3,'3',false);
  display.setChar(5,4,'4',false);
  display.setChar(5,5,'5',false);
  display.setChar(5,6,'6',false);
  display.setChar(5,7,'7',false);


  // Set the lights to blue to show we are connecting to the wifi
  setLEDIndicator(1, gLED_COLORINT, gLED_COLORINT, gLED_COLORINT);  // blue
  setLEDIndicator(2, gLED_COLORINT, gLED_COLORINT, gLED_COLORINT);  // blue
  setLEDIndicator(3, gLED_COLORINT, gLED_COLORINT, gLED_COLORINT);  // blue

  // Setup WiFi
  if (!wifi.init(SSID, PASSWORD, 9600))
  {
    #ifdef SERIAL_DEBUG
        Serial.println("Wifi Init failed. Check configuration.");
    #endif

    setLEDIndicator(1, gLED_COLORINT, gLED_COLORINT, 0);  // error
    setLEDIndicator(2, gLED_COLORINT, gLED_COLORINT, 0);  // error
    setLEDIndicator(3, gLED_COLORINT, gLED_COLORINT, 0);  // error

    // If we have a wifi error - then ask for reset
    displayMatrix(0, 10);
    displayMatrix(1, 10);
    displayMatrix(2, 10);
    displayMatrix(3, 10);

    delay(5000);
    asm volatile ("  jmp 0");     // Reset the program (not ideal but probably just about does the trick)
    
    while (true) ; // loop eternally
  }
  
  // To get us going, grab the data
  getPage();

  return;
}

//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
void displayMatrix(int disp, int img) // display an image from the IMAGES array (created using https://xantorohara.github.io/led-matrix-editor/)
{
  if(img < 0 || img > IMAGES_LEN) return;   // Return without doing anything if we are out of range

  uint64_t image = IMAGES[img]; // Read in our image from the array

  // Loop through each LED in the matrix (note: we also specify which of our displays we are setting)
  for (int i = 0; i < 8; i++) 
  {
    byte row = (image >> i * 8) & 0xFF;
    
    for (int j = 0; j < 8; j++) 
    {
      display.setLed(disp, i, j, bitRead(row, j));
    }
  }

  return;
}

//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
void setLEDIndicator(int which, int red, int green, int blue)
{

// NOTE! - I only want full red, or full green, or full blue - so I removed the analog writing which is needed for creating mixed colours! ************************

  #ifdef STATUS_LIGHTS
  
  if(which == 1)
  {
    if(red > 0) digitalWrite(red1Pin, HIGH); else digitalWrite(red1Pin, LOW);
    if(green > 0) digitalWrite(green1Pin, HIGH); else digitalWrite(green1Pin, LOW);
    if(blue > 0) digitalWrite(blue1Pin, HIGH); else digitalWrite(blue1Pin, LOW);
  }
  else if(which == 2)
  {
    if(red > 0) digitalWrite(red2Pin, HIGH); else digitalWrite(red2Pin, LOW);
    if(green > 0) digitalWrite(green2Pin, HIGH); else digitalWrite(green2Pin, LOW);
    if(blue > 0) digitalWrite(blue2Pin, HIGH); else digitalWrite(blue2Pin, LOW);
  }
  else if(which == 3)
  {
    if(red > 0) digitalWrite(red3Pin, HIGH); else digitalWrite(red3Pin, LOW);
    if(green > 0) digitalWrite(green3Pin, HIGH); else digitalWrite(green3Pin, LOW);
    if(blue > 0) digitalWrite(blue3Pin, HIGH); else digitalWrite(blue3Pin, LOW);
  }

  #endif

  return;
}


//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
void loop(void)
{   

    // Change the view every 2 seconds
    if(millis() - millis_last_view_change > INFO_REFRESH_TIME)
    {
      millis_last_view_change = millis();

      g_currentView++;
      if(g_currentView > VIEW__DAILY_SYSTEMS) g_currentView = VIEW__CURRENT_USERS; // Reset to our first view

      // Update the view
      updateView(g_currentView);
    }


    // Grab a page every minute
    if(millis() - millis_last_page_grab > REFRESH_TIME)
    {
      millis_last_page_grab = millis();

      #ifdef BLUE_LOADING_LIGHTS
      // set the colours to blue
      setLEDIndicator(1, 0, 0, gLED_COLORINT);
      setLEDIndicator(2, 0, 0, gLED_COLORINT);
      setLEDIndicator(3, 0, 0, gLED_COLORINT);
      #endif

      success = getPage();
    }

  return;
}

//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
void updateView(int whichView)
{
  if(g_nightmode == 1) return;
  
  int ths = 0;
  int hun = 0;
  int ten = 0;
  int dig = 0;

  if(whichView == VIEW__CURRENT_USERS)
  {
      // Current users (in last 60 seconds)

      // Work out the digit for each column (we have a max of 9999 - more users than that and I'll need to make a new dashboard!)
      ths = state_acu / 1000;
      hun = (state_acu - (ths * 1000)) / 100;
      ten = (state_acu - (ths * 1000) - (hun * 100)) / 10;
      dig = (state_acu - (ths * 1000) - (hun * 100) - (ten * 10));

      // If we have thousands, then show them, otherwise we show an explanation icon
      if(ths != 0)
      {
        displayMatrix(3, ths);
      }
      else
      {
        // our image for the info
        displayMatrix(3, 16);
      }

      // 100s
      if(hun != 0)
      {
        display.shutdown(2, false);
        displayMatrix(2, hun);
      }
      else display.clearDisplay(2); // or blank

      // 10s
      if(ten != 0)
      {
        display.shutdown(1, false);
        displayMatrix(1, ten);
      }
      else display.clearDisplay(1); // or blank

      // 0s (we always display this data)
      displayMatrix(0, dig);

  }
  if(whichView == VIEW__DAILY_USERS)
  {
      // Daily users (since 00:00)

      // Work out the digit for each column (we have a max of 9999 - more users than that and I'll need to make a new dashboard!)
      ths = state_dacu / 1000;
      hun = (state_dacu - (ths * 1000)) / 100;
      ten = (state_dacu - (ths * 1000) - (hun * 100)) / 10;
      dig = (state_dacu - (ths * 1000) - (hun * 100) - (ten * 10));

      // If we have thousands, then show them, otherwise we show an explanation icon
      if(ths != 0)
      {
        displayMatrix(3, ths);
      }
      else
      {
        // our image for the info
        displayMatrix(3, 17);
      }

      // 100s
      if(hun != 0)
      {
        display.shutdown(2, false);
        displayMatrix(2, hun);
      }
      else display.clearDisplay(2); // or blank

      // 10s
      if(ten != 0)
      {
        display.shutdown(1, false);
        displayMatrix(1, ten);
      }
      else display.clearDisplay(1); // or blank

      // 0s (we always display this data)
      displayMatrix(0, dig);

  }
  if(whichView == VIEW__CURRENT_SYSTEMS)
  {
      // Active systems (in last 60 seconds)

      // Work out the digit for each column (we have a max of 9999 - more users than that and I'll need to make a new dashboard!)
      ths = state_acs / 1000;
      hun = (state_acs - (ths * 1000)) / 100;
      ten = (state_acs - (ths * 1000) - (hun * 100)) / 10;
      dig = (state_acs - (ths * 1000) - (hun * 100) - (ten * 10));

      // If we have thousands, then show them, otherwise we show an explanation icon
      if(ths != 0)
      {
        displayMatrix(3, ths);
      }
      else
      {
        // our image for the info
        displayMatrix(3, 18);
      }

      // 100s
      if(hun != 0)
      {
        display.shutdown(2, false);
        displayMatrix(2, hun);
      }
      else display.clearDisplay(2); // or blank

      // 10s
      if(ten != 0)
      {
        display.shutdown(1, false);
        displayMatrix(1, ten);
      }
      else display.clearDisplay(1); // or blank

      // 0s (we always display this data)
      displayMatrix(0, dig);
  }
  if(whichView == VIEW__DAILY_SYSTEMS)
  {
      // Daily Active systems (since 00:00)

      // Work out the digit for each column (we have a max of 9999 - more users than that and I'll need to make a new dashboard!)
      ths = state_dacs / 1000;
      hun = (state_dacs - (ths * 1000)) / 100;
      ten = (state_dacs - (ths * 1000) - (hun * 100)) / 10;
      dig = (state_dacs - (ths * 1000) - (hun * 100) - (ten * 10));

      // If we have thousands, then show them, otherwise we show an explanation icon
      if(ths != 0)
      {
        displayMatrix(3, ths);
      }
      else
      {
        // our image for the info
        displayMatrix(3, 19);
      }

      // 100s
      if(hun != 0)
      {
        display.shutdown(2, false);
        displayMatrix(2, hun);
      }
      else display.clearDisplay(2); // or blank

      // 10s
      if(ten != 0)
      {
        display.shutdown(1, false);
        displayMatrix(1, ten);
      }
      else display.clearDisplay(1); // or blank

      // 0s (we always display this data)
      displayMatrix(0, dig);
  }


return;  
}


//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
int getPage()
{
  millis_last_page_grab = millis(); // update the last time we went off to get a page

  g_oldnightmode = g_nightmode;
  
  // The request
  char* request =  "GET /gp/dbd.php HTTP/1.1\r\nHost: 192.168.1.112\r\nConnection: close\r\n\r\n";

  // Connect to Server
  if (wifi.createTCP(SERVER_IP, SERVER_PORT))
  {

  }
  else
  {
#ifdef SERIAL_DEBUG
    Serial.println(F("create tcp - ERROR"));
#endif
    return "";
  }

  if (!wifi.sendSingle(request))
  {
#ifdef SERIAL_DEBUG
    Serial.print(F("not sent"));
#endif
    return 0;
  }

  int len = wifi.recv(wifi.m_responseBuffer, MAX_BUFFER_SIZE, 1000);

char stats[255];
char* stat = strstr((char*)wifi.m_responseBuffer,"|$|");
char* s2 = stat + 3;
#ifdef SERIAL_DEBUG
Serial.println((char*)s2);
#endif

// Read the data
int cStat = 1; // which current stat
char * pch;
pch = strtok (s2,"|");
state_s1 = atoi(pch);


// ****** So this next bit walks through the response from the server and puts the numbers into variables, the server sends us the data split by | characters
// ****** You could use this same technique in your dashboard, or some other way of seperating the values
// ****** The only key thing is that it is going to be consistent, and that you can interpret what you get from the server reliably


  while (pch != NULL)
  {
#ifdef SERIAL_DEBUG
    Serial.println(pch);
#endif
    pch = strtok (NULL, "|");
    if(cStat == 1) state_s2 = atoi(pch);
    if(cStat == 2) state_cj = atoi(pch);
    if(cStat == 3) state_dacu = atoi(pch);
    if(cStat == 4) state_dacs = atoi(pch);
    if(cStat == 5) state_acu = atoi(pch);
    if(cStat == 6) state_acs = atoi(pch);
    if(cStat == 7) g_nightmode = atoi(pch);
    cStat++;
  }



#ifdef SERIAL_DEBUG
Serial.println("S1: ");
Serial.println(state_s1);

Serial.println("S2: ");
Serial.println(state_s1);

Serial.println("ACU: ");
Serial.println(state_acu);

Serial.println("DACS: ");
Serial.println(state_dacs);
#endif


if(g_nightmode == 1 && state_s1 == 1 && state_s2 == 1) // we have gone into night mode (and both servers are okay)
{
  setLEDIndicator(1, 0, 0, 0);  // off
  setLEDIndicator(2, 0, 0, 0);  // off
  setLEDIndicator(3, 0, 0, 0);  // off

  display.clearDisplay(0);
  display.clearDisplay(3);

  display.shutdown(1, false);
  display.shutdown(2, false);
  displayMatrix(1, 21);
  displayMatrix(2, 20);

  for(int a = 4; a < 6; a++)
  {
    for(int b = 0; b < 8; b++)
    {
      display.setChar(a,b,' ',false);
    }
  }

}
else // NORMAL DAY MODE
{

  // Update the colours of our leds representing server statuses
  if(state_s1 == 1)
  {
    setLEDIndicator(1, 0, gLED_COLORINT, 0);  // red
  }
  else if(state_s1 == 0)
  {
    setLEDIndicator(1, gLED_COLORINT, 0, 0);  // red
  }
  else
  {
    setLEDIndicator(1, 0, 0, gLED_COLORINT);  // red
  }
  
  
  if(state_s2 == 1)
  {
    setLEDIndicator(2, 0, gLED_COLORINT, 0);  // red
  }
  else if(state_s2 == 0)
  {
    setLEDIndicator(2, gLED_COLORINT, 0, 0);  // red
  }
  else
  {
    setLEDIndicator(2, 0, 0, gLED_COLORINT);  // red
  }
  
  
  if(state_cj == 0) // as in okay!
  {
    setLEDIndicator(3, 0, gLED_COLORINT, 0);  // red
  }
  else if(state_cj > 0) // as in not okay!
  {
    setLEDIndicator(3, gLED_COLORINT, 0, 0);  // red
  }
  else // not set
  {
    setLEDIndicator(3, 0, 0, gLED_COLORINT);  // red
  }

  
  
  // ****** this next bit gets us from integer variables like 354, 1343, 23 etc.... to a series of characters that we can then display in their relevant locations
  
  // ***** Note: there are easier ways to do this - I chose this method as it shows clearly what we are doing and works well for a tutorial
  
  int disp = 4;

  int number = state_dacs;
  char res[8]; // 8 max
  for(int a = 0; a < 8; a++)
  {
    res[a] = ' ';
  }

  int ths, hun, ten, dig;

  ths = number / 1000;
  res[0] = '0' + ths;
  if(ths == 0) res[0] = ' ';
  hun = (number- (ths * 1000)) / 100;
  res[1] = '0' + hun;
  if(ths == 0 && hun == 0) res[1] = ' ';
  ten = (number - (ths * 1000) - (hun * 100)) / 10;
  res[2] = '0' + ten;
  if(ths == 0 && hun == 0 && ten == 0) res[2] = ' ';
  dig = (number - (ths * 1000) - (hun * 100) - (ten * 10));
  res[3] = '0' + dig;
  res[4] = 0;  

  display.setChar(disp,7,res[0],false);
  display.setChar(disp,6,res[1],false);
  display.setChar(disp,5,res[2],false);
  display.setChar(disp,4,res[3],false);



  number = state_dacu;
  for(int a = 0; a < 8; a++)
  {
    res[a] = ' ';
  }

  ths = number / 1000;
  res[0] = '0' + ths;
  if(ths == 0) res[0] = ' ';
  hun = (number- (ths * 1000)) / 100;
  res[1] = '0' + hun;
  if(ths == 0 && hun == 0) res[1] = ' ';
  ten = (number - (ths * 1000) - (hun * 100)) / 10;
  res[2] = '0' + ten;
  if(ths == 0 && hun == 0 && ten == 0) res[2] = ' ';
  dig = (number - (ths * 1000) - (hun * 100) - (ten * 10));
  res[3] = '0' + dig;
  res[4] = 0;  

  display.setChar(disp,3,res[0],false);
  display.setChar(disp,2,res[1],false);
  display.setChar(disp,1,res[2],false);
  display.setChar(disp,0,res[3],false);


disp = 5; // last one

  number = state_acu;
  for(int a = 0; a < 8; a++)
  {
    res[a] = ' ';
  }

  ths = number / 1000;
  res[0] = '0' + ths;
  if(ths == 0) res[0] = ' ';
  hun = (number- (ths * 1000)) / 100;
  res[1] = '0' + hun;
  if(ths == 0 && hun == 0) res[1] = ' ';
  ten = (number - (ths * 1000) - (hun * 100)) / 10;
  res[2] = '0' + ten;
  if(ths == 0 && hun == 0 && ten == 0) res[2] = ' ';
  dig = (number - (ths * 1000) - (hun * 100) - (ten * 10));
  res[3] = '0' + dig;
  res[4] = 0;  

  display.setChar(disp,7,res[0],false);
  display.setChar(disp,6,res[1],false);
  display.setChar(disp,5,res[2],false);
  display.setChar(disp,4,res[3],false);


  number = state_acs;
  for(int a = 0; a < 8; a++)
  {
    res[a] = ' ';
  }

  ths = number / 1000;
  res[0] = '0' + ths;
  if(ths == 0) res[0] = ' ';
  hun = (number- (ths * 1000)) / 100;
  res[1] = '0' + hun;
  if(ths == 0 && hun == 0) res[1] = ' ';
  ten = (number - (ths * 1000) - (hun * 100)) / 10;
  res[2] = '0' + ten;
  if(ths == 0 && hun == 0 && ten == 0) res[2] = ' ';
  dig = (number - (ths * 1000) - (hun * 100) - (ten * 10));
  res[3] = '0' + dig;
  res[4] = 0;  

  display.setChar(disp,3,res[0],false);
  display.setChar(disp,2,res[1],false);
  display.setChar(disp,1,res[2],false);
  display.setChar(disp,0,res[3],false);
}

  return len;
}

Comments

Similar projects you might like

IR Home Automation on DFRobot's Relay Shield

Project tutorial by Techduino

  • 461 views
  • 0 comments
  • 5 respects

Sesame

Project showcase by gibatronic

  • 5,160 views
  • 7 comments
  • 13 respects

Aurdino Radar With Processing

Project in progress by Akshay6766

  • 1,595 views
  • 1 comment
  • 10 respects

Laundry IFTTT Alert

Project tutorial by danvanf

  • 997 views
  • 0 comments
  • 5 respects

Workspace Environment Monitor - enVMon

Project showcase by Team comcrazy

  • 749 views
  • 2 comments
  • 9 respects

Butterfly Alarm Clock

Project tutorial by Patrick Prescott

  • 1,062 views
  • 0 comments
  • 5 respects
Add projectSign up / Login