XIAO ESP32-C5 - 5 G...
 
Notifications
Clear all

XIAO ESP32-C5 - 5 GHz Wi-Fi IoT Powerhouse

5 Posts
5 Users
1 Reactions
649 Views
(@dronebot-workshop)
Workshop Guru Admin
Joined: 7 years ago
Posts: 1186
Topic starter  

Build a Dual-Band Wi-Fi Analyzer using the XIAO ESP32-C5, the first XIAO with 5GHz Wi-Fi!

It’s a new Seeeduino XIAO, and it’s an inexpensive IoT powerhouse! The Seeeduino XIAO ESP32-C5 is one of the first development boards powered by the amazing Espressif ESP32-C5, a microcontroller built with IoT in mind.

The ESP32-C5 is a powerful, single-core 32-bit MCU that runs at 240 MHz. It is one of the first ESP32 chips to support Wi-Fi 6, and has both 2.4 and 5 GHz Wi-Fi. It also supports BLE 5, Matter, Zigbee, and Thread.

It also features a power management system that can put the board into Deep Sleep to drastically improve battery life. And speaking of batteries, the XIAO ESP32 has a built-in battery charger, so you can run everything from a 3.7-volt LiPo or LION battery.

We’ll take a look at this new microcontroller board, and then we’ll put our knowledge to use and build a dual-channel Wi-Fi signal analyzer with an LCD screen. It's a really fast project, since we can just modify some sample code from the display graphics library.

Here is the content of today's video:

00:00 - Introduction
01:45 - XIAO ESP32-C5
06:10 - IDE Setup and Testing
07:43 - XIAO ESP32-C5 Connectivity
13:31- XIAO ESP32-C5 Low Power
18:36 - Build a Dual-Band Wi-Fi Scanner
23:43 - Conclusion

This powerful board retails for under 7 US dollars, so it certainly won’t break the bank. With its multiple connectivity options and low-power features, this is sure to become the heart of many innovative IoT projects.

I hope you enjoy the video!

Bill


"Never trust a computer you can’t throw out a window." — Steve Wozniak


   
robotBuilder reacted
Quote
Oliver2003
(@oliver2003)
Member
Joined: 4 months ago
Posts: 12
 

This looks like an exciting board! The jump to 5GHz on such a small module really opens up a lot of possibilities for faster and more reliable IoT connections.

 

Speaking of IoT builds, if you're looking for some inspiration for your next project, I recently came across this guide on PCBWay about building a home automation system with a microcontroller. It has some great design tips and ideas that might pair well with this new XIAO board: https://www.pcbway.com/project/shareproject/Best_IOT_Project_Making_At_Home_using_Microcontroller_PCBway_531d1012.html

 

Has anyone here managed to get their hands on one of these yet? I'm curious to see how the 5GHz range holds up in a real-world environment!


A Electrical engineer who loves the robots and drones


   
ReplyQuote
(@bobbuilt)
Member
Joined: 2 months ago
Posts: 4
 

Excellent video! Definitely going to build that WiFi analyzer.

 



   
ReplyQuote
Yassin
(@yassin)
Member
Joined: 4 months ago
Posts: 41
 
Hi Bill,
 
Just finished your XIAO ESP32-C5 video and it’s such a great deep dive—this little board is an absolute steal for under $7, and Seeed really nailed packing all that ESP32-C5 functionality into the XIAO form factor. Dual-band Wi-Fi 6, BLE 5, plus Matter/Zigbee/Thread support is a killer combo for compact IoT builds, and the onboard LiPo charging + deep sleep power management? Total game-changer for battery-powered projects, exactly what we need for small-form-factor IoT gear.
 
Loved the dual-band Wi-Fi analyzer build too—tweaking the display library sample code makes it the perfect first project to test that 5GHz capability right out the gate. From what I’ve tinkered with the ESP32-C5 core, its 5GHz performance is surprisingly solid for its size (way better than I expected), even if it’s a bit sensitive to physical obstructions like most 5GHz modules. This analyzer build is perfect for real-world testing that, such a smart use of the board’s strengths.
 
I had one quick thought/question while watching that I’m curious to hear your take on: the single-core 240MHz MCU is more than enough for basic IoT use cases, but have you tried running concurrent connectivity (Wi-Fi 6 + Zigbee/Thread) with a light sensor load on it yet? Wondering how it handles the multi-protocol overhead—my hunch is it runs smooth for the compact builds this board’s designed for, but it’d be awesome to hear your real-world experience with it.
 
This board’s already shot to the top of my parts list for upcoming small IoT projects—thanks for the fantastic breakdown and practical build guide, it’s got me fired up to tinker with one!

 

Yassin


Yassin | Building Compact, High-Current Connections for Drones & Robots


   
ReplyQuote
(@ad8bc)
Member
Joined: 6 months ago
Posts: 5
 

UPDATE:  Nevermind.  I was trying to use the SEEED XIAO boards instead of the ESP32 C5 board in the boards manager.  Sometimes it pays to go all the way back through Bill's video instead of going directly to the project timestamp.  

So I've put together the project and I've set it up correctly in the Arduino IDE and I've got the example project loaded and I'm trying to download to the XIAO C5.  I keep getting the following errors:

C:\Users\BillCahill\AppData\Local\arduino\sketches\75114BB97358C836E4891CAE3759B138/..\..\cores\f7cc6c3b2f58c6c0b3acf17392e2ba8c\core.a(main.cpp.o): In function `_real_body()':
C:\Users\BillCahill\AppData\Local\Arduino15\packages\Seeeduino\hardware\samd\1.8.5\cores\arduino/main.cpp:40: undefined reference to `setup'
C:\Users\BillCahill\AppData\Local\Arduino15\packages\Seeeduino\hardware\samd\1.8.5\cores\arduino/main.cpp:43: undefined reference to `loop'
collect2.exe: error: ld returned 1 exit status
exit status 1

Compilation error: exit status 1

I've gone through the code and I see the void setup() and void loop() routines and they seem to have the brackets in the correct places.  I've tried bringing in the example code a few times.  What am I missing here?

Thanks for your help!

For reference:

/*******************************************************************************
 * ESP32-C5 WiFi Analyzer
 * Requires ESP32-C5 board
 ******************************************************************************/
#if CONFIG_SOC_WIFI_SUPPORT_5G

// POWER SAVING SETTING
#define SCAN_INTERVAL 3000
// #define SCAN_COUNT_SLEEP 5
// #define LCD_PWR_PIN 0
// #define LCD_PWR_PIN1 1
// #define LCD_PWR_PIN2 2
// #define LCD_PWR_PIN3 3

/*******************************************************************************
 * Start of Arduino_GFX setting
 *
 * Arduino_GFX try to find the settings depends on selected board in Arduino IDE
 * Or you can define the display dev kit not in the board list
 * Defalult pin list for non display dev kit:
 * ESP32-C5 various dev board  : CS: 23, DC: 24, RST: 25, BL: 26, SCK: 10, MOSI:  8, MISO: nil
 ******************************************************************************/
#include <Arduino_GFX_Library.h>

#define GFX_BL 1 // default backlight pin, you may replace DF_GFX_BL to actual backlight pin

/* More dev device declaration:  https://github.com/moononournation/Arduino_GFX/wiki/Dev-Device-Declaration  */
#if defined(DISPLAY_DEV_KIT)
Arduino_GFX *gfx = create_default_Arduino_GFX();
#else /* !defined(DISPLAY_DEV_KIT) */

/* More data bus class:  https://github.com/moononournation/Arduino_GFX/wiki/Data-Bus-Class  */
Arduino_DataBus *bus = create_default_Arduino_DataBus();

/* More display class:  https://github.com/moononournation/Arduino_GFX/wiki/Display-Class  */
Arduino_GFX *gfx = new Arduino_ILI9341(bus, DF_GFX_RST, 1 /* rotation */, false /* IPS */);

#endif /* !defined(DISPLAY_DEV_KIT) */
/*******************************************************************************
 * End of Arduino_GFX setting
 ******************************************************************************/

#include <WiFi.h>

int16_t w, h, banner_text_size, banner_height, graph24_baseline, graph50_baseline, graph_baseline, graph_height, channel24_width, channel50_width, signal_width;

// RSSI RANGE
#define RSSI_CEILING -30
#define RSSI_SHOW_SSID -70
#define RSSI_FLOOR -100

// Channel legend mapping
uint8_t channel_legend[] = {
    1, 2, 3, 4, 5, 6, 7,     // 0-6:     1,  2,  3,  4,  5,  6,  7,
    8, 9, 10, 11, 12, 13, 0, // 7-13:    8,  9, 10, 11, 12, 13, 14,
    0, 0, 36, 0, 0, 0, 44,   // 14-20:  32, 34, 36, 38, 40, 42, 44,
    0, 0, 0, 52, 0, 0, 0,    // 21-27:  46, 48, 50, 52, 54, 56, 58,
    60, 0, 0, 0, 68, 0, 0,   // 28-34:  60, 62, 64,N/A, 68,N/A,N/A,
    0, 0, 0, 0, 0, 0, 0,     // 35-41: N/A,N/A,N/A,N/A,N/A,N/A, 96,
    0, 100, 0, 0, 0, 108, 0, // 42-48: N/A,100,102,104,106,108,110,
    0, 0, 116, 0, 0, 0, 124, // 49-55: 112,114,116,118,120,122,124,
    0, 0, 0, 132, 0, 0, 0,   // 56-62: 126,128,N/A,132,134,136,138,
    140, 0, 0, 0, 149, 0, 0, // 63-69: 140,142,144,N/A,149,151,153,
    0, 157, 0, 0, 0, 165, 0, // 70-76: 155,157,159,161,163,165,167,
    0, 0, 173, 0, 0, 0};     // 77-82: 169,171,173,175,177,N/A

// Channel color mapping
uint16_t channel_color[] = {
    RGB565_RED, RGB565_ORANGE, RGB565_YELLOW, RGB565_LIME, RGB565_CYAN, RGB565_DODGERBLUE, RGB565_MAGENTA,
    RGB565_RED, RGB565_ORANGE, RGB565_YELLOW, RGB565_LIME, RGB565_CYAN, RGB565_DODGERBLUE, RGB565_MAGENTA,
    RGB565_RED, RGB565_ORANGE, RGB565_YELLOW, RGB565_LIME, RGB565_CYAN, RGB565_DODGERBLUE, RGB565_MAGENTA,
    RGB565_RED, RGB565_ORANGE, RGB565_YELLOW, RGB565_LIME, RGB565_CYAN, RGB565_DODGERBLUE, RGB565_MAGENTA,
    RGB565_RED, RGB565_ORANGE, RGB565_YELLOW, RGB565_WHITE, RGB565_CYAN, RGB565_WHITE, RGB565_WHITE,
    RGB565_WHITE, RGB565_WHITE, RGB565_WHITE, RGB565_WHITE, RGB565_WHITE, RGB565_WHITE, RGB565_MAGENTA,
    RGB565_WHITE, RGB565_ORANGE, RGB565_YELLOW, RGB565_LIME, RGB565_CYAN, RGB565_DODGERBLUE, RGB565_MAGENTA,
    RGB565_RED, RGB565_ORANGE, RGB565_YELLOW, RGB565_LIME, RGB565_CYAN, RGB565_DODGERBLUE, RGB565_MAGENTA,
    RGB565_RED, RGB565_ORANGE, RGB565_WHITE, RGB565_LIME, RGB565_CYAN, RGB565_DODGERBLUE, RGB565_MAGENTA,
    RGB565_RED, RGB565_ORANGE, RGB565_YELLOW, RGB565_WHITE, RGB565_CYAN, RGB565_DODGERBLUE, RGB565_MAGENTA,
    RGB565_RED, RGB565_ORANGE, RGB565_YELLOW, RGB565_LIME, RGB565_CYAN, RGB565_DODGERBLUE, RGB565_MAGENTA,
    RGB565_RED, RGB565_ORANGE, RGB565_YELLOW, RGB565_LIME, RGB565_CYAN, RGB565_WHITE};

uint8_t scan_count = 0;

uint16_t channelIdx(int channel)
{
  if (channel <= 14) // 2.4 GHz, channel 1-14
  {
    return channel - 1;
  }
  if (channel <= 68) // 5 GHz, channel 32 - 64
  {
    return 14 + ((channel - 32) / 2);
  }
  if (channel <= 144) // channel 96 - 144
  {
    return 41 + ((channel - 96) / 2);
  }
  if (channel <= 177) // channel 149 - 177
  {
    return 67 + ((channel - 149) / 2);
  }
  return 82;
}

void setup()
{
#if defined(LCD_PWR_PIN)
  pinMode(LCD_PWR_PIN, OUTPUT);    // sets the pin as output
  digitalWrite(LCD_PWR_PIN, HIGH); // power on
#endif
#if defined(LCD_PWR_PIN)
  pinMode(LCD_PWR_PIN1, OUTPUT);    // sets the pin as output
  pinMode(LCD_PWR_PIN2, OUTPUT);    // sets the pin as output
  pinMode(LCD_PWR_PIN3, OUTPUT);    // sets the pin as output
  digitalWrite(LCD_PWR_PIN1, HIGH); // power on
  digitalWrite(LCD_PWR_PIN2, HIGH); // power on
  digitalWrite(LCD_PWR_PIN3, HIGH); // power on
#endif

#ifdef DEV_DEVICE_INIT
  DEV_DEVICE_INIT();
#endif

  Serial.begin(115200);
  // Serial.setDebugOutput(true);
  // while(!Serial);
  Serial.println("Arduino_GFX ESP32-C5 WiFi Analyzer UTF8 example");

  // Enable Station Interface
  WiFi.STA.begin();

  // Init Display
  if (!gfx->begin())
  {
    Serial.println("gfx->begin() failed!");
  }
  gfx->fillScreen(RGB565_BLACK);

#ifdef GFX_BL
  pinMode(GFX_BL, OUTPUT);
  digitalWrite(GFX_BL, HIGH);
#endif

  w = gfx->width();
  h = gfx->height();
  banner_text_size = (w < 444) ? 1 : 2;
  banner_height = (banner_text_size * 8) + 2;
  graph_height = ((h - banner_height) / 2) - 20; // minus 2 text lines
  graph24_baseline = banner_height + graph_height;
  graph50_baseline = graph24_baseline + 20 + graph_height;
  channel24_width = w / (14 + 2);
  channel50_width = w / (sizeof(channel_legend) - 14 + 4);

  // init banner
  gfx->fillRect(0, 0, w, banner_text_size * 8, RGB565_PURPLE);
  gfx->setTextSize(banner_text_size);
  gfx->setCursor(0, 0);
  gfx->setTextColor(RGB565_WHITE, RGB565_RED);
  gfx->print(" ESP32");
  gfx->setTextColor(RGB565_WHITE, RGB565_DARKORANGE);
  gfx->print("-C5 ");
  gfx->setTextColor(RGB565_WHITE, RGB565_LIMEGREEN);
  gfx->print(" Dual Band ");
  gfx->setTextColor(RGB565_WHITE, RGB565_MEDIUMBLUE);
  gfx->print(" WiFi ");
  gfx->setTextColor(RGB565_WHITE, RGB565_PURPLE);
  gfx->print(" Analyzer");
  gfx->setTextSize(1);

#ifdef CANVAS
  gfx->flush();
#endif
}

bool matchBssidPrefix(uint8_t *a, uint8_t *b)
{
  for (uint8_t i = 0; i < 5; i++)
  { // only compare first 5 bytes
    if (a[i] != b[i])
    {
      return false;
    }
  }
  return true;
}

void loop()
{
  uint16_t ap24_count = 0, ap50_count = 0;
  uint8_t ap_count_list[sizeof(channel_legend)];
  int32_t peak_list[sizeof(channel_legend)];
  int16_t peak_id_list[sizeof(channel_legend)];
  int32_t channel;
  int16_t idx;
  int32_t rssi;
  uint8_t *bssid;
  String ssid;
  uint16_t color;
  int16_t height, offset, text_width;

  // init array value
  for (int i = 0; i < sizeof(channel_legend); ++i)
  {
    ap_count_list[i] = 0;
    peak_list[i] = RSSI_FLOOR;
    peak_id_list[i] = -1;
  }

  WiFi.setBandMode((scan_count < 1) ? WIFI_BAND_MODE_2G_ONLY : WIFI_BAND_MODE_AUTO);
  // WiFi.setBandMode(WIFI_BAND_MODE_5G_ONLY);

  // WiFi.scanNetworks will return the number of networks found
  int n = WiFi.scanNetworks(false /* async */, true /* show_hidden */, false /* passive */, (scan_count < 2) ? 30 : 300 /* max_ms_per_chan */);

  // clear old graph
  gfx->fillRect(0, banner_height, w, h - banner_height, RGB565_BLACK);

  if (n == 0)
  {
    gfx->setTextColor(RGB565_WHITE);
    gfx->setCursor(0, banner_height);
    gfx->println("No networks found");
  }
  else
  {
    for (int i = 0; i < n; i++)
    {
      channel = WiFi.channel(i);
      idx = channelIdx(channel);
      rssi = WiFi.RSSI(i);
      bssid = WiFi.BSSID(i);
      ssid = WiFi.SSID(i);

      // channel peak stat, find peak ssid
      if (ssid.length() > 0)
      {
        if (peak_list[idx] < rssi)
        {
          peak_list[idx] = rssi;
          peak_id_list[idx] = i;
        }
      }

      // check signal come from same AP
      bool duplicate_SSID = false;
      for (int j = 0; j < i; j++)
      {
        if ((WiFi.channel(j) == channel) && matchBssidPrefix(WiFi.BSSID(j), bssid))
        {
          duplicate_SSID = true;
          break;
        }
      }

      if (!duplicate_SSID)
      {
        ap_count_list[idx]++;
        if (channel <= 14)
        {
          ap24_count++;
        }
        else
        {
          ap50_count++;
        }
      }
    }
    Serial.printf("ap24_count: %d, ap50_count: %d\n", ap24_count, ap50_count);

    // plot found WiFi info
    for (int i = 0; i < n; i++)
    {
      channel = WiFi.channel(i);
      idx = channelIdx(channel);
      rssi = WiFi.RSSI(i);
      color = channel_color[idx];
      height = constrain(map(rssi, RSSI_FLOOR, RSSI_CEILING, 1, graph_height), 1, graph_height);
      if (idx < 14)
      {
        graph_baseline = graph24_baseline;
        signal_width = channel24_width * 2;
        offset = (idx + 2) * channel24_width;
      }
      else
      {
        graph_baseline = graph50_baseline;
        signal_width = channel50_width * 4;
        offset = (idx - 14 + 4) * channel50_width;
      }

      // trim rssi with RSSI_FLOOR
      if (rssi < RSSI_FLOOR)
      {
        rssi = RSSI_FLOOR;
      }

      // plot chart
      // gfx->drawLine(offset, graph_baseline - height, offset - signal_width, graph_baseline + 1, color);
      // gfx->drawLine(offset, graph_baseline - height, offset + signal_width, graph_baseline + 1, color);
      gfx->startWrite();
      gfx->writeEllipseHelper(offset, graph_baseline + 1, signal_width, height, 0b0011, color);
      gfx->endWrite();

      if ((rssi >= RSSI_SHOW_SSID) && (i == peak_id_list[idx]))
      {
        // Print SSID, signal strengh and if not encrypted
        ssid = WiFi.SSID(i);
        if (ssid.length() == 0)
        {
          ssid = WiFi.BSSIDstr(i);
        }
        text_width = (ssid.length() + 6) * 6;
        if (text_width > w)
        {
          offset = 0;
        }
        else
        {
          offset -= signal_width;
          if ((offset + text_width) > w)
          {
            offset = w - text_width;
          }
        }
        gfx->setTextColor(color);
        gfx->setCursor(offset, ((height + 8) > graph_height) ? (graph_baseline - graph_height) : (graph_baseline - 10 - height));
        gfx->print(ssid);
        gfx->print('(');
        gfx->print(rssi);
        gfx->print(')');
        if (WiFi.encryptionType(i) == WIFI_AUTH_OPEN)
        {
          gfx->print('*');
        }
      }
    }
  }

  // draw 2.4 GHz graph base axle
  gfx->drawFastHLine(0, graph24_baseline, w, RGB565_WHITE);
  for (idx = 0; idx < 14; idx++)
  {
    channel = channel_legend[idx];
    offset = (idx + 2) * channel24_width;
    if (channel > 0)
    {
      gfx->setTextColor(channel_color[idx]);
      gfx->setCursor(offset - ((channel < 10) ? 3 : 6), graph24_baseline + 2);
      gfx->print(channel);
    }
    if (ap_count_list[idx] > 0)
    {
      gfx->setTextColor(RGB565_LIGHTGREY);
      gfx->setCursor(offset - ((ap_count_list[idx] < 10) ? 3 : 6), graph24_baseline + 8 + 2);
      gfx->print(ap_count_list[idx]);
    }
  }

  // draw 5 GHz graph base axle
  gfx->drawFastHLine(0, graph50_baseline, w, RGB565_WHITE);
  for (idx = 14; idx < sizeof(channel_legend); idx++)
  {
    channel = channel_legend[idx];
    offset = (idx - 14 + 4) * channel50_width;
    if (channel > 0)
    {
      gfx->setTextColor(channel_color[idx]);
      gfx->setCursor(offset - ((channel < 100) ? 6 : 9), graph50_baseline + 2);
      gfx->print(channel);
    }
    if (ap_count_list[idx] > 0)
    {
      gfx->setTextColor(RGB565_LIGHTGREY);
      gfx->setCursor(offset - ((ap_count_list[idx] < 10) ? 3 : 6), graph50_baseline + 8 + 2);
      gfx->print(ap_count_list[idx]);
    }
  }
  gfx->setTextColor(RGB565_MEDIUMBLUE, RGB565_BLACK);
  gfx->setCursor(w - 18, graph24_baseline + 8 + 2);
  gfx->printf("%03d", ap24_count);
  gfx->setTextColor(RGB565_LIMEGREEN, RGB565_BLACK);
  gfx->setCursor(w - 18, graph50_baseline + 8 + 2);
  gfx->printf("%03d", ap50_count);

  gfx->setTextSize(2);
  gfx->setTextColor(RGB565_WHITE, RGB565_MEDIUMBLUE);
  gfx->setCursor(0, graph24_baseline + 1);
  gfx->print("2.4");
  gfx->setTextColor(RGB565_WHITE, RGB565_LIMEGREEN);
  gfx->setCursor(0, graph50_baseline + 1);
  gfx->print("5");
  gfx->setTextSize(1);

#ifdef CANVAS
  gfx->flush();
#endif

  // Wait a bit before scanning again
  delay((scan_count < 2) ? 0 : SCAN_INTERVAL);
  ++scan_count;

#if defined(SCAN_COUNT_SLEEP)
  // POWER SAVING
  if (scan_count >= SCAN_COUNT_SLEEP)
  {
#if defined(LCD_PWR_PIN)
    pinMode(LCD_PWR_PIN, INPUT); // disable pin
#endif
#if defined(LCD_PWR_PIN1)
    pinMode(LCD_PWR_PIN1, INPUT); // disable pin
    pinMode(LCD_PWR_PIN2, INPUT); // disable pin
    pinMode(LCD_PWR_PIN3, INPUT); // disable pin
#endif

#if defined(GFX_BL)
    pinMode(GFX_BL, INPUT); // disable pin
#endif

    esp_deep_sleep_start();
  }
#endif // defined(SCAN_COUNT_SLEEP)
}

#endif // #if CONFIG_SOC_WIFI_SUPPORT_5G

 


This post was modified 3 weeks ago by AD8BC

   
ReplyQuote