The infrastructure behind

I built as I regularly play Smite game with friends. The initial aim was to create a clan based smite stats site just for fun. However it has evolved into quite the project and stores around 4GB of player data including over 5,400,000 match records.

Smite stats relies on the official Hi-Rez Smite, Paladins and Realm Developer API (more details can be found here). The API provides a source for all player and match data. Consequently if the API became unavailable then the site would cease to provide real time data. In that case it would be more of an archive of smite stats data.

I set the site up using Symfony 5 and a large set of custom entities that model the incoming API data. A mapping class then formats this raw json into an entity prior to persisting to the database with Doctrine.

Minimising API calls

The API has a daily usage limit, Hi-Rez will increase the limits over time if you speak to them and demo a suitable use case. If you limit the amount of calls you make to the API then there’s no reason to do this. For instance this site will cache all API responses with as long a cache TTL as possible. When generating the latest players for a match the site queries the matches endpoint. It then checks if those matches are in the database and stores them if not. Using a matches_updated property on the player entity this is set to the current datetime. The recent matches API query only happens again after fifteen minutes have elapsed. This is particularly helpful to avoid high API usage when a search engine bot crawls the site.

Currently the site is using Redis to cache in memory in the server. It has a suitable memory allocation with a cache eviction policy set to allkeys-lru. This Redis policy means old cache entries are removed to free up space for new entries. For this site this policy works well as the cache items are disposable.


The site uses source control via Github and the repository is private with separate branches per feature.

For the server it uses a suitable AWS EC2 instance combined with Elastic Storage to accommodate for data storage needs. I have found EC2 paired with Route 53 for DNS zone management very suitable for small projects.

One limit of the current setup is the lack of automated deployments. A deploy at the moment consists of a PR merge to master. Followed by a cache clear and yarn install. In the future this process will improve with the implementation of Jenkins jobs and/or Ansible playbooks.


To monitor the system I have setup AWS CloudWatch. This integrates well with EC2 and effectively syncs the logs and custom metrics as required.

One of the most useful features is the ability to almost near time remotely monitor the storage, CPU and memory levels. Alarms and events can then be trigger to warn of production critical issues. It also comes in handy when debugging historical events as you have a full system usage graph history.


Currently the MySQL database is local on the EC2 instance. This has issues because it doesn’t scale as well. Ideally the MySQL database would be remote on another instance with it’s own environment configuration. An even better solution would be to use Docker containers but on initial setup I wasn’t too familiar with that approach combined with EC2. Something to improve and blog about in the future.

The database is has version control via the aforementioned Symfony Entities combined with Doctrine migrations. I find this to be a very effective approach to easily storing and managing entities.

In total the database has 16 tables, these are all fully relational with correct data types, indexes and foreign keys. As of writing the largest table, the match_player_item has ~28,590,000 entries. This table alone consists of around 982MB data around what items a player made use of in a match. However size wise the match_player is the largest and ~5,467,000 entries store 2.1GB. Some may argue this is too large for one table but the data maps 1-1 with the match player API response. To separate this data further would only increase the complexity for the mappers to little gain.

Smite stats mysql database


The front-end of the site is of a minimal design (design creation has never been my strong point). Bootstrap 4 is in use for the markup and design style. I wrote some custom css to give it more of a Smite feel. Chart.Js is in use for the graphs but they could do with more customisation. The Smite build generator tool makes use of AngularJS as it requires more dynamic data. However as AngularJS has been deprecated React may be a possible to replace that at some point. Lastly webpack encore is in use with yarn to manage and install the dependencies.


Overall this covers the setup used for the site. There are areas that could do with improvement but I feel the above is a solid setup for a relatively small website.

So go and install Smite via Steam, Xbox One or PS4 and join us in the game! You’ll then have a perfect platform to check your playing stats to see if you’re any good.

I also welcome feedback on the site and suggestions for improvement. The whole purpose was to create a communal Smite stats site that benefits all those who make use of it.

Arduino RC Tamiya – Part 3 – Adding Wifi Control

Last time we replaced the standard receiver with a custom coded Arduino. This allowed us to program the car ourselves and run simple automated scripts to turn around in a circle. In this post we’ll enhance our remote control car so it can be driven over WiFi.

The Arduino board we have been using is WiFi ready, due to its secure ECC608 crypto chip accelerator. This is perfect as we can use the readily available Arduino WiFi library in our code.

Tamiya Arduino WiFi Setup

The first step is to write some basic code to check the Arduino can connect to the Internet. In the following example, we’ll be connecting through a home router but you could hotspot your phone and connect directly. (If you’re using source control remember not to add your WiFi credentials!)

#include <SPI.h>
#include <WiFi.h>

char ssid[] = "yourNetworkName";  // your network SSID (name)
char pass[] = "secretPassword";   // your network password

int status = WL_IDLE_STATUS;

void setup() {

  // check for the presence of the WiFi module:
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println("Communication with WiFi module failed!");
    // don't continue:
    while (true);

  String fv = WiFi.firmwareVersion();
  if (fv != "1.1.0") {
    Serial.println("Please upgrade the firmware");

  // attempt to connect to Wifi network:
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");

    // Connect to WPA/WPA2 network
    status = WiFi.begin(ssid, pass);

    // wait 10 seconds for connection:


void printWifiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");

  // print your board's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");

  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.println(" dBm");

The above code will connect to a WiFi SSID of your choice with a given password. Next it checks your Arduino has an available WiFi module and a supported firmware version. After that it tries to establish a connection. The debugging information about the connection will be recorded via the serial port output, so you can monitor the progress.

Tamiya Arduino WiFi Control

Now we have a successful WiFi connection it’s time to allow the Arduino controlled car to receive some commands and start moving.

To allow the car to receive commands we will program the Arduino to operate as a web server. It will host a web page available via the WiFi shield’s IP address that will have just enough HTML to control the digital Arduino pin inputs.

The following demo gives us the ability to steer left or right, it’s important to start simple and when confident add more control. This helps avoid breaking the car, precious household objects and people in close vicinity!

#include <Servo.h>
#include <SPI.h>
#include <WiFiNINA.h>

Servo steering;
WiFiServer server(80);

char ssid[] = "yourNetworkName";  // your network SSID (name)
char pass[] = "secretPassword";   // your network password

// Include a web page template with the UI car control buttons
const char *webpage = 
#include "uipage.h"

int leftTurn = 0;
int rightTurn = 180;
int status = WL_IDLE_STATUS;

void setup() {

  // check for the WiFi module:
  // removed for cleaner demo, view above snippet
  // check the firmware version
  // removed for cleaner demo, view above snippet

  // attempt to connect to Wifi network:
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to Network named: ");

    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    status = WiFi.begin(ssid, pass);
    // wait 10 seconds for connection:
  // start the web server on port 80

void loop() {
  // listen for incoming clients
  WiFiClient client = server.available();   
  if (client) {                             // if you get a client,
    String currentLine = "";                // make a String to hold incoming data from the client

    while (client.connected()) {            // loop while the client's connected
      if (client.available()) {             // if there's bytes to read from the client,
        char c =;             // read a byte, then
        Serial.write(c);                    // print it out via the serial monitor
        if (c == '\n') {                   
          // if the current line is blank, you get two newline characters in a row.
          // that's the end of the client HTTP request, so send a response:
          if (currentLine.length() == 0) {
            // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
            // and a content-type so the client knows what's coming, then a blank line:
            client.println("HTTP/1.1 200 OK");

            // the content of the HTTP response follows the header:
            // this is where we load our HTML UI template
            // HTTP response ends with another blank line:
          } else {    // if you got a newline, then clear currentLine:
            currentLine = "";
        } else if (c != '\r') {  // if anything but a carriage return character,
          currentLine += c;

        // check if client request was "GET /left"
        if (currentLine.endsWith("GET /left")) {

        // check if client request was "GET /right"
        if (currentLine.endsWith("GET /right")) {
    Serial.println("Client disconnected");

The following code is the HTML UI template. This is what we load in the above code to allow user input for sending commands to the car. For now we only have a left and right button. Bootstrap and jQuery are included to allow for pretty buttons and Ajax request handling.

<!DOCTYPE html>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <link rel="stylesheet" href="">
    <div class="container-fluid">
      <div class="row">
        <div class="col-xs-6">
          <button id="left" type="button" class="btn btn-default"
        <div class="col-xs-6">
          <button id="right" type="button" class="btn btn-default" 
    <script src=""></script>
    <script src=""></script>
        function makeAjaxCall(url) {
            $.ajax({"url": url});

With the above loaded onto your Arduino and running you should be able to navigate to the Arduino cars IP address in your browser and see the UI.

WiFi Remote Control Tamiya UI Web Server

In the above, clicking on either the left or right button will send a HTTP request to the running web server. The Arduino reads this request and converts the url path into an action that changes the Arduino pin value and the servos move. If all has gone well you should be able to steer your RC Tamiya via the Arduino through the web served UI!


Tune in for the next post where we’ll add more remote control functionality. At that point it gets a bit more complex as you try to mimic the remote control logic of an actual RC car. For instance steering straight on release of the left or right button and being able to stop and reverse.

Before I go, here’s a quick demo of forward and reverse working that will be coming very soon! Although I need to find a better way of sharing my phone screen and recording at the same time…