Track statistics in WoT with Arduino



Hi, Habr. Remembering his engineering youth, I wanted to fiddle with the glands again. It was frankly lazy to return to PIC'acm and programming on ACME (traumatic childhood memories of manual work without third-party libraries with the i2c and HD44780 buses), so I went online and found out about the existence of the Arduino platform.

A quick glance showed that this is exactly what I need to satisfy nostalgic urges. A week ago, a kit was purchased. The former electronic engineer with a five-year experience flashing a LED somehow not comme il faut, that's why the idea of ​​a statistic meter in tanks was born (I confess, it’s a sin ...). This is my kind of "Hello, Habr!" and "Hello, World!"

The idea is simple - to monitor the Wargaming statistics server and notify the user about his game successes (or turnaround). I decided to monitor using the Wargaming API, since the idea of ​​parsing web pages of extraneous resources rested on the RAM size (8kb per reception) used to access the shield on the W5100 controller.

To display the information I took what was at hand - the standard display 1602. But that would not be too trivial, I decided to connect it through the shift register 74HC595. A quick search proved the old truth that everything has been invented before us: link. The display sits on the SPI bus and additionally uses one pin to select a device (I have a 3rd one). For successful compilation, you need to replace the LiquidCrystal library with the modified one from the link above. Also, from the link above, the author forgot to put the r / w pin of the display on the ground, because of which he initially thought that the example was not working. Just in case, my crazy board:
Breadboard

I also wanted to implement achievement logging on an SD card, but decided to leave it for the future, as well as a small web server with monitoring results.

Sketch
#include
#include
#include

#define ETHERNET_PIN 10
#define LCD_PIN 3

// Forced IP address on the network
IPAddress ip (192, 168, 1, 40);

// MAC
byte mac [] = {0x42,0x42,0x42,0x42,0x42,0x42};

// Server, registered ideshnik, profile number
const char server [] = "api.worldoftanks.ru";
const char application_id [] = "demo";
const char account_id [] = "4848655";

// Update frequency in milliseconds
const unsigned int UpdateDelta = 20000;
// Left until the update
unsigned int UpdateTime = 0;

// Network client
EthernetClient client;
// LCD
LiquidCrystal lcd (LCD_PIN);

byte newChar1 [8] = {
B00100,
B01110,
B10101,
B00100,
B00100,
B00100,
B00100,
B00000
};

byte newChar2 [8] = {
B00100,
B00100,
B00100,
B00100,
B10101,
B01110,
B00100,
B00000
};

//
Parsing state enum ReadStatusEnum {
WiteTag,
ReadTag,
WiteValue
} ReadStatus = WiteTag;

String nickname = "Unknown Noob";
long WG_rating, pre_WG_rating, buf_WG_rating = 0;
unsigned long battles, pre_battles, buf_battles = 0;
unsigned long wins, pre_wins, buf_wins = 0;
unsigned long damage, pre_damage, buf_damage = 0;
unsigned long frags, pre_frags, buf_frags = 0;

String LCD_strings [6];

//
Search for the desired void FilterData (String SectionTagName, String TagName, String Value)
{
if (SectionTagName.compareTo ("statistics") == 0) {
if (TagName.compareTo ("nickname") == 0)
nickname = Value
if (TagName.compareTo ("global_rating") == 0)
buf_WG_rating = Value.toInt ();
}
if (SectionTagName.compareTo ("all") == 0) {
if (TagName.compareTo ("battles") == 0)
buf_battles = Value.toInt ();
if (TagName.compareTo ("wins") == 0)
buf_wins = Value.toInt ();
if (TagName.compareTo ("damage_dealt") == 0)
buf_damage = Value.toInt ();
if (TagName.compareTo ("frags") == 0)
buf_frags = Value.toInt ();
}
}

void readServer ()
{
// Connect
if (client.connect (server, 80)) {
client.print ("GET / wot / account / info /? application_id =");
client.print (application_id);
client.print ("& account_id =");
client.print (account_id);
client.println ("HTTP / 1.1");
client.print ("Host:„);
client.println (server);
client.println (“User-Agent: arduino-ethernet”);
client.println ("Accept: text / html");
client.println ("Connection: close");
client.println ();

// We are waiting for readiness
bool Wait = true;
unsigned int Time = 0;
while (Wait)
{
Wait =! client.available ();
if (Time> 200) Wait = false;
Time ++;
delay (10);
}

char symbol = '';
char pre_symbol = '';
String TagName = "";
String SectionTagName = "";
String Value = "";
String PreSectionTagName = "";

ReadStatus = WiteTag;

// Repeat until the buffer is empty
while (client.available ()) {
// Read the byte from the buffer
pre_symbol = symbol;
symbol = client.read ();
//Serial.print(symbol);
if (ReadStatus == WiteTag && pre_symbol == '"' && symbol! = ':'


TagName = "";
}

if (ReadStatus == ReadTag) {
if (symbol! = '"') {
// Read the tag name
TagName + = symbol;
}
else
{
// Found the end of the tag
Value =" ";
ReadStatus = WiteValue;
}
}

if ( ReadStatus == WiteValue) {
if (symbol == ',' || symbol == '}') {
// End of value
ReadStatus = WiteTag;
FilterData (SectionTagName, TagName, Value);
if (symbol == '}') {
// End of section
SectionTagName = PreSectionTagName;
}
}
else {
if (symbol == '{') {
// Start of section
PreSectionTagName = SectionTagName;
SectionTagName = TagName;
ReadStatus = WiteTag;
}
else {
// Read the value
if (symbol! = ':' && symbol! = '"' && symbol! = '')
Value + = symbol;
}
}
}
}
}
// Stop the client
client.flush ();
client .stop ();
}

void generateSrings ()
{
if (pre_battles == 0 || battles == 0) {
// Initialize
pre_WG_rating = buf_WG_rating;
WG_rating = buf_WG_rating;
pre_battles = buf_battles;
battles = buf_battles;
pre_wins = buf_wins;
wins = buf_wins;
pre_damage = buf_damage;
damage = buf_damage;
pre_frags = buf_frags;
frags = buf_frags;
}

if (buf_battles> battles)
{
// There was an update to the stats
pre_WG_rating = WG_rating;
WG_rating = buf_WG_rating;
pre_battles = battles;
battles = buf_battles;
pre_wins = wins;
wins = buf_wins;
pre_damage = damage;
damage = buf_damage;
pre_frags = frags;
frags = buf_frags;
}

if (pre_battles == battles)
{
LCD_strings [0] = nickname + "";
LCD_strings [0] = LCD_strings [0] .substring (0,16);

LCD_strings [1] = "Btl:" + String (battles) + "-";
LCD_strings [1] = LCD_strings [1] .substring (0,16);

float wins_percent = (float) wins / (float) battles * 100.0;
LCD_strings [2] = "Wins:" + String (wins_percent) + "-";
LCD_strings [2] = LCD_strings [2] .substring (0,16);

float avrage_damage = (float) damage / (float) battles;
String D = String (avrage_damage);
D = D.substring (0, D.length () - 1);
LCD_strings [3] = "Dmg:" + D + "-";
LCD_strings [3] = LCD_strings [3] .substring (0,16);

float avrage_frags = (float) frags / (float) battles;
LCD_strings [4] = "Frag:" + String (avrage_frags) + "-";
LCD_strings [4] = LCD_strings [4] .substring (0,16);

LCD_strings [5] = "WG:" + String (WG_rating) + "-";
LCD_strings [5] = LCD_strings [5] .substring (0,16);
}
else
{
LCD_strings [0] = nickname + "";
LCD_strings [0] = LCD_strings [0] .substring (0,16);

LCD_strings [1] = "Btl:" + String (battles) + "" + char (0x01) + String (battles-pre_battles) + "";
LCD_strings [1] = LCD_strings [1] .substring (0,16);

float wins_percent = (float) wins / (float) battles * 100.0;
float pre_wins_percent = (float) pre_wins / (float) pre_battles * 100.0;
char Delta = char (0x01);
if (wins_percent <pre_wins_percent) Delta = char (0x02);
LCD_strings [2] = "Wins:" + String (wins_percent) + "" + Delta + String (abs (wins_percent - pre_wins_percent)) + "";
LCD_strings [2] = LCD_strings [2].

float avrage_damage = (float) damage / (float) battles;
float pre_avrage_damage = (float) pre_damage / (float) pre_battles;
Delta = char (0x01);
if (avrage_damage <pre_avrage_damage) Delta = char (0x02);
String D = String (avrage_damage);
D = D.substring (0, D.length () - 1);
LCD_strings [3] = "Dmg:" + D + "" + Delta + String (abs (avrage_damage - pre_avrage_damage)) + "";
LCD_strings [3] = LCD_strings [3] .substring (0,16);

float avrage_frags = (float) frags / (float) battles;
float pre_avrage_frags = (float) pre_frags / (float) pre_battles;
Delta = char (0x01);
if (avrage_frags <pre_avrage_frags) Delta = char (0x02);
LCD_strings [4] = "Frag:" + String (avrage_frags) + "" + Delta + String (abs (avrage_frags - pre_avrage_frags)) + "";
LCD_strings [4] = LCD_strings [4] .substring (0,16);

Delta = char (0x01);
if (WG_rating <pre_WG_rating) Delta = char (0x02);
LCD_strings [5] = "WG:" + String (WG_rating) + "" + Delta + String (abs (WG_rating - pre_WG_rating)) + "";
LCD_strings [5] = LCD_strings [5] .substring (0,16);
}
}

void PrintMSG (int LCD_tick, int ScrNum)
{
if (LCD_tick <= 16)
{
lcd.setCursor (0, 0);
lcd.print (LCD_strings [ScrNum * 2 + 0] .substring (0, LCD_tick) + "_");
}
else
{
lcd.setCursor (0, 1);
lcd.print (LCD_strings [ScrNum * 2 + 1] .substring (0, LCD_tick-16) + "_");
}
}

unsigned int LCD_Screen = 0;
unsigned int LCD_tick = 0;
void UpdateLCD (unsigned int UpdateTime)
{
if (UpdateTime> UpdateDelta / 3 * 2)
{
// First screen
if (LCD_Screen! = 0) {
LCD_Screen = 0;
LCD_tick = 0;
}
PrintMSG (LCD_tick, LCD_Screen);
}
else
{
if (UpdateTime> UpdateDelta / 3)
{
// Second screen
if (LCD_Screen! = 1) {
LCD_Screen = 1;
LCD_tick = 0;
}
PrintMSG (LCD_tick, LCD_Screen);
}
else
{
// Third screen
if (LCD_Screen! = 2) {
LCD_Screen = 2;
LCD_tick = 0;
}
PrintMSG (LCD_tick, LCD_Screen);
}
}

LCD_tick ++;
if (LCD_tick> 32) LCD_tick = 32;
}

void setup ()
{
// Initialize the display
lcd.createChar (1, newChar1);
lcd.createChar (2, newChar2);
lcd.begin (16, 2);

// Initialization of the serial port
//Serial.begin(57600);
pinMode (ETHERNET_PIN, OUTPUT);
digitalWrite (ETHERNET_PIN, LOW);
delay (1000);
Ethernet.begin (mac, ip);
delay (1000);
digitalWrite (ETHERNET_PIN, HIGH);
}

void loop ()
{
// Request a statue
if (UpdateTime == 0) {
UpdateTime = UpdateDelta;
digitalWrite (ETHERNET_PIN, LOW);
readServer ();
digitalWrite (ETHERNET_PIN, HIGH);
generateSrings ();
}

UpdateLCD (UpdateTime);
delay (100);
UpdateTime - = 100;
}

And here is a demonstration of work. Unfortunately, Wargaming servers update statistics only after the end of the game session:



And finally, a fly in the ointment - for some reason I don’t understand, after some time, the device stops taking statistics from the server. The debug and indicators show that the request is leaving, but there is no answer. I hope this is because of my crooked router.
Upd. Already not relevant, fixed sketch

Also popular now: