PlatformIO Community

Please help with this stock ticker

Good morning!
I found a stock ticker program I like. I want to make it an ESP Now sender. It looks to me like it should work, but I get three errors, one for each stock I’m trying to look up. I got the API Key.
It looks to me like a query comes up empty.
“Fetching price for TSLA
Fetching resource from https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol
=TSLAQITRTT5A746YIJBI
HTTPS GET failed with code -1
Failed to parse response to JSON with EmptyInput
Fetching price for NLY
Fetching resource from https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol
=NLYQITRTT5A746YIJBI
HTTPS GET failed with code -1
Failed to parse response to JSON with EmptyInput
Fetching price for BREW
Fetching resource from https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol
=BREWQITRTT5A746YIJBI
HTTPS GET failed with code -1
Failed to parse response to JSON with EmptyInput
Fetching resource from https://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_R
ATE&from_currency=BTC&to_currency=USDQITRTT5A746YIJBI
HTTPS GET failed with code -1
Failed to parse response to JSON with EmptyInput
Fetching price for TSLA
Fetching resource from https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol
=TSLAQITRTT5A746YIJBI
HTTPS GET failed with code -1
Failed to parse response to JSON with EmptyInput
Fetching price for NLY
Fetching resource from https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol
=NLYQITRTT5A746YIJBI
HTTPS GET failed with code -1
Failed to parse response to JSON with EmptyInput
Fetching price for BREW
Fetching resource from https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol
=BREWQITRTT5A746YIJBI
HTTPS GET failed with code -1
Failed to parse response to JSON with EmptyInput”

Here’s the code:

#include <Arduino.h>

#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#include <ArduinoJson.h>

#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>

#include <ESP8266HTTPClient.h>

#include <WiFiClientSecureBearSSL.h>

// Wifi credential information
const String kSsid = "TP-Link_7C28";
const String kPassword = "64411811";

// AlphaVantage API information, they have free tokens ;)
const String kApiUri = "https://www.alphavantage.co";
const String kApiBaseUri = "https://www.alphavantage.co/query?";
const String kApiStockPrice = "function=GLOBAL_QUOTE&";
const String kApiExchangeRate = "function=CURRENCY_EXCHANGE_RATE&";
const String kApiFromCurrency = "from_currency=BTC&";
// Note: we leave the & off of this one as the last piece is the kApiKey
const String kApiToCurrency = "to_currency=USD";
const String kApiSymbol = "symbol=";
const String kApiKey = "QITRTT5A746YIJBI";

// Define additional ticker symbols here
const String kTickerSymbols[] = {
    "TSLA",   // 
    "NLY", // 
};

// Max document size that we can parse for JSON, resp from AV has about 10 vals
const size_t kMaxJsonDoc = 0x400;
StaticJsonDocument<kMaxJsonDoc> doc;

// Main loop pause
const size_t kDelay = 30000;
// Seconds to wait between POSTs to AlphaVantage
const size_t kStockPause = 15000;
// Blink delay for the lights
const size_t kBlinkDelay = 1000;

// SHA1 fingerprint of the sites certificate
const char *kAlphaVantageFingerprint = "C9 44 AD B9 48 6F 7C D1 64 5F ED 9B 49 53 56 88 DA FD FC 4B";

// Init the SSD1306 display
Adafruit_SSD1306 display = Adafruit_SSD1306();

// Init the ESP8266 Wifi Module
ESP8266WiFiMulti WiFiMulti;

void setup()
{
  Serial.begin(9600);

  Serial.println();
  Serial.println("Stock Ticker Board v0.1");
  Serial.print("Connecting to wifi: ");
  Serial.print(kSsid);

  // initialize with the I2C addr 0x3C (for the 128x32)
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.display();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  auto msg = "Connecting to " + kSsid;
  display.print(msg);
  display.setCursor(0, 0);
  display.display();
  delay(500);

  WiFi.mode(WIFI_STA);
  WiFiMulti.addAP(kSsid.c_str(), kPassword.c_str());

  while (WiFiMulti.run() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(" .");
  }

  Serial.println();
  Serial.print("Connected to ");
  Serial.print(kSsid);
  Serial.print(" with IP address: ");
  Serial.println(WiFi.localIP());
  display.setCursor(0, 15);
  display.print("IP: " + WiFi.localIP().toString());
  display.display();
  delay(2000);
}

// Fetches a string response from a remote API endpoint URI
int getApiResponse(const String &uri, String &resp)
{
  Serial.println("Fetching resource from " + uri);
  std::unique_ptr<BearSSL::WiFiClientSecure> secure_client(new BearSSL::WiFiClientSecure);

  secure_client->setFingerprint(kAlphaVantageFingerprint);
  secure_client->setTimeout(3000);

  HTTPClient https;
  auto ret = https.begin(*secure_client, uri);

  if (!ret)
  {
    Serial.println("Failed to connect to URI with " + String(ret));
    return 1;
  }

  auto status = https.GET();
  if (status != HTTP_CODE_OK)
  {
    Serial.println("HTTPS GET failed with code " + String(status));
    return status;
  }

  resp = https.getString();
  return status;
}

void blink()
{
  digitalWrite(LED_BUILTIN, HIGH);
  delay(kBlinkDelay);
  digitalWrite(LED_BUILTIN, LOW);
  delay(kBlinkDelay);
}

void loop()
{

  // Blink to indicate we're about to fetch new stocks
  blink();

  // We fetch stock values in a loop around the ticker symbols, defined above
  for (const auto sym : kTickerSymbols)
  {
    Serial.println("Fetching price for " + sym);

    // Perform the API request
    String resp;
    String uri = kApiBaseUri + kApiStockPrice + kApiSymbol + sym + kApiKey;
    auto ret = getApiResponse(uri, resp);
    if (ret == 1)
    {
      Serial.println("Fetch remote resource failed. Continuing");
      delay(kStockPause);
      continue;
    }

    // Parse the response into a JSON object
    auto err = deserializeJson(doc, resp);
    if (err)
    {
      Serial.println("Failed to parse response to JSON with " + String(err.c_str()));
      delay(kStockPause);
      continue;
    }

    // We display the price
    auto quote = doc["Global Quote"];
    auto quote_msg = sym + ": " + quote["05. price"].as<String>() + ", " +
                     quote["09. change"].as<String>() + " (" +
                     quote["10. change percent"].as<String>() + ")";
    Serial.println(quote_msg);
    display.clearDisplay();
    display.setCursor(0, 0);
    display.print(quote_msg);
    display.display();
    delay(kStockPause);
  }

  // Lastly, fetch the USD -> BTC exchange rate, for funsies
  String resp;
  String uri = kApiBaseUri + kApiExchangeRate + kApiFromCurrency +
               kApiToCurrency + kApiKey;
  auto ret = getApiResponse(uri, resp);

  // Parse the response into a JSON object
  auto err = deserializeJson(doc, resp);
  if (err)
  {
    Serial.println("Failed to parse response to JSON with " + String(err.c_str()));
  }
  else
  {
    // We display the price
    auto er = doc["Realtime Currency Exchange Rate"];
    auto msg = er["1. From_Currency Code"].as<String>() + " -> " +
               er["3. To_Currency Code"].as<String>() + ": " +
               er["5. Exchange Rate"].as<String>();
    Serial.println(msg);
    display.clearDisplay();
    display.setCursor(0, 0);
    display.print(msg);
    display.display();
    delay(kDelay);
  }
}

and another thing:
“{
“Error Message”: “the parameter apikey is invalid or missing. Please claim your free API key on (Customer Support | Alpha Vantage). It should take less than 20 seconds.”
}”

But when I look at the certificate info on that page in my browser it says

grafik

So the SHA1 doesn’t seem to match the source code anymore. Certificate info says it was changed 1 month ago in the “validity” section.

grafik

So what happens if you say

// SHA1 fingerprint of the sites certificate
const char *kAlphaVantageFingerprint = "27 F4 DF 78 11 04 1A DB 4B EC 5A 4C D5 0A FE 1F D6 24 CE BF";

instead?

Wow, that changes things a lot. But this is the return:
Fetching price for TSLA
Fetching resource from https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol
=TSLAQITRTT5A746YIJBI
TSLA: null, null (null)
Fetching price for NLY
Fetching resource from https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol
=NLYQITRTT5A746YIJBI
NLY: null, null (null)

There are two errors, and neither gets tripped:

auto ret = getApiResponse(uri, resp);

    if (ret == 1)

    {

      Serial.println("Fetch remote resource failed. Continuing");

      delay(kStockPause);

      continue;

    }

    // Parse the response into a JSON object

    auto err = deserializeJson(doc, resp);

    if (err)

    {

      Serial.println("Failed to parse response to JSON with " + String(err.c_str()));

      delay(kStockPause);

      continue;

    }

But that code

with

Is weird because it doesn’t properly give it the API key. Per API Documentation | Alpha Vantage it must be in a GET parameter called apikey. It just pastes it after the symbol of the stock.

When it should be

https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=NLY&apikey=QITRTT5A746YIJBI

Can you change

to

const String kApiKey = "&apikey=QITRTT5A746YIJBI";

?

DUDE!
I have to say that this is a relationship I really value. You’re my imaginary friend.
It works.

So the declaration of const String kApiKey doesn’t initialize with the value but a pointer to that value? Or is it that making it a string requires pointing?

No pointers involved. & In the context of a url is just a parameter separator.