Friday, June 28, 2019

Discovering the Goldmine that is the M5Stack Arduino Environment

When I last played with my M5Stack Fire device I had figured out a development environment and had my code for my first trivial app written. The app, if you can even call it that, accepted button input and made web requests to a particular URL with the button label appended. I was able to get both button detection and URL invocation working. Alas, when I combined these operations into one program the result was a system crash. Such a pain.

Out of frustration I put the device down planning to return it with fresh eyes.

This week I picked up the device and thought I'd try another approach to programming it. I'd had a recollection that the Arduino IDE had M5Stack support. A quick Google search turned up a YouTube video that promised I'd have this environment setup in less than 5 minutes.

Impressively, the video wasn't far off from its 5 minute promise. There were a bunch of steps, but everything fell into place and before I knew it, I'd configured the Arduino dev environment to work with my Fire.

But the part that blew me away was all the example code that's available:

For a good 20 minutes I was a kid in a candy store: opening up demos, running them, and ooh-and-ahhing as my M5 came to life. Bluetooth, WiFi, microphone support, LED support--there's code to demonstrate it all. If I can see the individual examples running, I should be able to mix and match the pieces into interesting projects. So. Cool.

Returning to my original challenge, I used the Display, Button and BasicHttpClient examples to work up my little button triggered URL app.

I ran into one gotcha during the process. At some point, attempts to flash new code to the device resulted in the cryptic message:

Arduino: 1.8.9 (Mac OS X), Board: "M5Stack-FIRE, Enabled, Default (2 x 6.5 MB app, 3.6 MB SPIFFS), 115200, None"

Sketch uses 344988 bytes (5%) of program storage space. Maximum is 6553600 bytes.
Global variables use 16604 bytes (0%) of dynamic memory, leaving 4505380 bytes for local variables. Maximum is 4521984 bytes.
esptool.py v2.6
Serial port /dev/cu.SLAB_USBtoUART
Connecting........_____....._
Chip is ESP32D0WDQ6 (revision 1)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None
MAC: 84:0d:8e:25:aa:34
Uploading stub...
Running stub...
Stub running...
Configuring flash size...
Warning: Could not auto-detect Flash size (FlashID=0xffffff, SizeID=0xff), defaulting to 4MB
Compressed 8192 bytes to 47...

A fatal error occurred: Timed out waiting for packet content
A fatal error occurred: Timed out waiting for packet content

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

I eventually arrived at a simple fix: hold down the power button on the device while Arduino is compiling and flashing the code. Easy peasy.

You might be wondering, what good is having a small device where a button press triggers a URL call? Thanks to Tasker and AutoRemote, it's possible to make your phone take essentially any action in response to a web request to a special public URL. For example, I setup a trivial Tasker profile that plays an ear splitting sound when button B is pressed on the M5. This is useful when I've put my phone down in my house and lost track of it; something that seems to happen constantly.

While I'm excited that I've got my first baby M5Stack project completed I'm even more excited to have this mountain of demo code to play with. Now the real fun can begin!

Here's the code to power the behavior described above, as well as some action shots. Enjoy!

/*
 * Pressy!
 */
#include <M5Stack.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include <HTTPClient.h>

WiFiMulti WiFiMulti;

#define AUTOREMOTE_BASE String("https://autoremotejoaomgcd.appspot.com/sendmessage")
#define AUTOREMOTE_KEY String("--auto remote key goes here--")
#define AUTOREMOTE_PREFIX String("Pressy_")

void clearScreen() {
  M5.Lcd.clear(BLACK);
  M5.Lcd.setCursor(0,0);
  M5.Lcd.setTextColor(YELLOW);
  M5.Lcd.println("Ready: ");
  M5.Lcd.setTextColor(WHITE);
}

void autoRemote(String btn) {
  String url = AUTOREMOTE_BASE + "?key=" + AUTOREMOTE_KEY + "&message=" + AUTOREMOTE_PREFIX + btn;
  HTTPClient http;
  M5.Lcd.print(btn + "...");
  http.begin(url);
  M5.Lcd.println(http.GET());
  http.end();
}

void setup() {
  // init lcd, serial, but don't init sd card
  M5.begin(true, false, true);
  M5.Lcd.clear(BLACK);
  M5.Lcd.setTextColor(YELLOW);
  M5.Lcd.setTextSize(2);
  M5.Lcd.setCursor(0, 0);
  M5.Lcd.println("Pressy");
  M5.Lcd.print("Setting up WiFi:");

  WiFiMulti.addAP("SSID", "PASSWORD");

  M5.Lcd.print(".");
  while(WiFiMulti.run() != WL_CONNECTED) {
    M5.Lcd.print(".");
    delay(250);
  }

  clearScreen();
}

// Add the main program code into the continuous loop() function
void loop() {
  M5.update();

  // if you want to use Releasefor("was released for"), use .wasReleasefor(int time) below
  if (M5.BtnA.wasReleased()) {
    autoRemote("A");
  } else if (M5.BtnB.wasReleased()) {
    autoRemote("B");
  } else if (M5.BtnC.wasReleased()) {
    autoRemote("C");
  } else if (M5.BtnB.wasReleasefor(700)) {
    clearScreen();
  }
}

No comments:

Post a Comment