Druk
Published © GPL3+

HELIOS Space Weather Intel

HELIOS: real-time space weather console using NOAA Space Weather Prediction Center data for GNSS, SATCOM & grid risk insights.

BeginnerFull instructions provided9
HELIOS Space Weather Intel

Things used in this project

Hardware components

M5StickC PLUS ESP32-PICO Mini IoT Development Kit
M5Stack M5StickC PLUS ESP32-PICO Mini IoT Development Kit
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Custom parts and enclosures

Schematics

This project uses the M5StickC Plus as an all-in-one microcontroller with built-in display, WiFi, and power management. No additional wiring is required.

Schematics

Schematic

Code

HELIOS Space Weather Intel

C/C++
1) Flash this code onto an M5StickC Plus / Plus 2 using Arduino IDE.
2) Enter your WiFi credentials below:
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
3) Power on the device.
4) The system connects to WiFi and fetches NOAA Kp index data every 5 minutes.

This badge is designed for:
- Space weather awareness
- Aviation / RF / satellite enthusiasts
- Educational demos & live showcases
- Embedded systems experimentation
#include <M5Unified.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <WiFiClientSecure.h>
#include <ArduinoJson.h>
#include <time.h>

// ---------------- WIFI ----------------
const char* ssid = ""; //SSID
const char* password = ""; //Password

// ---------------- DATA ----------------
float kp = 0;
float kp_prev = 0;
float riskIndex = 0;

String gnssStatus, hfStatus, ionosphereState, auroraStatus;

String flareClass = "A";

String kpTimeFormatted = "--:--";
String kpDateFormatted = "--";
String sysTimeStr = "--:--";

unsigned long lastFetch = 0;

// ---------------- COLORS ----------------
#define CYAN 0x07FF
#define WHITE 0xFFFF
#define BLACK 0x0000
#define YELLOW 0xFFE0
#define ORANGE 0xFD20

// ---------------- BOOT ----------------
void heliosBoot() {

  M5.Lcd.fillScreen(BLACK);

  for (int i = 0; i <= 100; i += 5) {

    M5.Lcd.setTextSize(2);
    M5.Lcd.setTextColor(CYAN);
    M5.Lcd.setCursor(30, 40);
    M5.Lcd.println("HELIOS");

    M5.Lcd.drawRect(20, 90, 200, 10, WHITE);
    M5.Lcd.fillRect(20, 90, i * 2, 10, CYAN);

    delay(25);
    M5.Lcd.fillScreen(BLACK);
  }

  M5.Lcd.setTextSize(1);
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.setCursor(45, 60);
  M5.Lcd.println("SYSTEM READY");

  delay(800);
}

// ---------------- BATTERY ----------------
void drawBattery() {

  int level = M5.Power.getBatteryLevel();  

  int x = 200, y = 5;

  M5.Lcd.drawRect(x, y, 30, 10, WHITE);

  int fill = (level * 28) / 100;

  uint16_t c = CYAN;
  if (level < 40) c = YELLOW;
  if (level < 20) c = ORANGE;

  M5.Lcd.fillRect(x + 1, y + 1, fill, 8, c);
}

// ---------------- TIME ----------------
void updateTime() {

  struct tm t;
  if (getLocalTime(&t)) {
    char buf[20];
    strftime(buf, sizeof(buf), "%d-%m %H:%M", &t);
    sysTimeStr = String(buf);
  }
}

// ---------------- KP FETCH ----------------
void fetchKp() {

  WiFiClientSecure client;
  client.setInsecure();

  HTTPClient http;
  http.begin(client,
    "https://services.swpc.noaa.gov/json/planetary_k_index_1m.json");

  if (http.GET() != 200) {
    http.end();
    return;
  }

  String payload = http.getString();
  http.end();

  DynamicJsonDocument doc(16384);
  deserializeJson(doc, payload);

  JsonObject last = doc[doc.size() - 1];

  kp = last["kp_index"].as<float>();

  String t = last["time_tag"].as<String>();

  if (t.length() >= 16) {
    kpDateFormatted = t.substring(8, 10) + "-" + t.substring(5, 7);
    kpTimeFormatted = t.substring(11, 16);
  }

  lastFetch = millis();
}

// ---------------- FLARE ----------------
void fetchFlare() {

  WiFiClientSecure client;
  client.setInsecure();

  HTTPClient http;
  http.begin(client,
    "https://services.swpc.noaa.gov/json/goes/primary/xrays-7-day.json");

  if (http.GET() != 200) {
    http.end();
    return;
  }

  String payload = http.getString();
  http.end();

  DynamicJsonDocument doc(32768);
  deserializeJson(doc, payload);

  JsonObject last = doc[doc.size() - 1];

  float flux = last["flux"].as<float>();

  if (flux < 1e-6) flareClass = "A";
  else if (flux < 1e-5) flareClass = "B";
  else if (flux < 1e-4) flareClass = "C";
  else if (flux < 1e-3) flareClass = "M";
  else flareClass = "X";
}

// ---------------- RISK ----------------
void computeRisk() {

  riskIndex = 0;

  riskIndex += kp * 12;

  if (flareClass == "B") riskIndex += 5;
  else if (flareClass == "C") riskIndex += 10;
  else if (flareClass == "M") riskIndex += 20;
  else if (flareClass == "X") riskIndex += 35;

  float delta = kp - kp_prev;
  riskIndex += delta * 8;

  if (riskIndex > 100) riskIndex = 100;
  if (riskIndex < 0) riskIndex = 0;

  kp_prev = kp;

  if (riskIndex < 30) {
    gnssStatus = "Nominal";
    hfStatus = "Stable";
    ionosphereState = "Quiet";
    auroraStatus = "Low";
  }
  else if (riskIndex < 60) {
    gnssStatus = "Degraded";
    hfStatus = "Disturbed";
    ionosphereState = "Active";
    auroraStatus = "Moderate";
  }
  else {
    gnssStatus = "Severe";
    hfStatus = "Blackout";
    ionosphereState = "Storm";
    auroraStatus = "High";
  }
}

// ---------------- RENDER ----------------
void render() {

  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setTextSize(1);

  // HEADER
  M5.Lcd.setTextColor(CYAN);
  M5.Lcd.setCursor(5, 5);
  M5.Lcd.println("HELIOS SPACE WEATHER INTEL");

  drawBattery();

  M5.Lcd.drawLine(0, 15, 240, 15, WHITE);

  // RISK
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.setCursor(5, 25);
  M5.Lcd.print("RISK: ");
  M5.Lcd.print((int)riskIndex);
  M5.Lcd.println(" / 100");

  // TABLE ROW 1
  M5.Lcd.setCursor(5, 45);
  M5.Lcd.print("GNSS - ");
  M5.Lcd.print(gnssStatus);

  M5.Lcd.setCursor(120, 45);
  M5.Lcd.print("HF - ");
  M5.Lcd.println(hfStatus);

  // TABLE ROW 2
  M5.Lcd.setCursor(5, 60);
  M5.Lcd.print("IONO - ");
  M5.Lcd.print(ionosphereState);

  M5.Lcd.setCursor(120, 60);
  M5.Lcd.print("AURORA - ");
  M5.Lcd.println(auroraStatus);

  M5.Lcd.drawLine(0, 80, 240, 80, WHITE);

  // TIME ROW
  M5.Lcd.setTextColor(CYAN);

  M5.Lcd.setCursor(5, 90);
  M5.Lcd.print("KP: ");
  M5.Lcd.print(kpDateFormatted);
  M5.Lcd.print(" ");
  M5.Lcd.println(kpTimeFormatted);

  M5.Lcd.setCursor(5, 105);
  M5.Lcd.print("TIME: ");
  M5.Lcd.println(sysTimeStr);

  M5.Lcd.setCursor(5, 120);
  M5.Lcd.print("DATA: ");
  M5.Lcd.println((millis() - lastFetch < 300000) ? "LIVE" : "STALE");
}

// ---------------- SETUP ----------------
void setup() {

  auto cfg = M5.config(); 
  M5.begin(cfg);   

  M5.Lcd.setRotation(1);

  heliosBoot();

  WiFi.begin(ssid, password);
  delay(2000);

  configTime(0, 0, "pool.ntp.org");

  fetchKp();
  fetchFlare();
  updateTime();

  computeRisk();
  render();
}

// ---------------- LOOP ----------------
void loop() {

  static unsigned long last = 0;

  if (millis() - last > 300000) {

    fetchKp();
    fetchFlare();
    updateTime();

    computeRisk();
    render();

    last = millis();
  }

  delay(50);
}

Credits

Druk
4 projects • 1 follower

Comments