M5Widgets: Widgets for the M5Stack

This is based on the [alas now defunct!] Phoenard project. The Phoenard had a touchscreen, whereas the M5Stack has a “dumb” screen. So instead of copy/pasting and adapting the code, I am selecting the juicy, non-touch bits, and adapting them to the M5, while simplifying a bit.


It is a little more complex than the regular library, as I am integrating this to the M5Stack library itself. The code repository is here. The first step is to drop all the .cpp and .h files (except Free_fonts.h, which is for the sample sketches) to the ~/Arduino/libraries/M5Stack/src/utility/ folder. Then, in M5Stack.h, towards the bottom of the file, after:

extern M5Stack M5;
#define m5 M5
#define lcd Lcd

add the following

// added by Kongduino
#include "utility/M5DataBuffer.h"
#include "utility/M5Widget.h"
#include "utility/M5Gauge.h"
#include "utility/M5ProgressBar.h"
#include "utility/M5BarGraph.h"
#include "utility/M5LineGraph.h"
#include "utility/M5Touch.h"
#include "utility/M5QRCode.h"

You have now access to the M5Widgets. Yay!

I am adding two [similar] examples: M5_Widgets.ino and M5_Widgets_BMP280.ino. The first one displays random data points, whereas the second one uses a BMP280 sensor to display temperature and pressure data in various widgets. They both use the Free_Fonts.h header file. Make copies, and put then in folders. You know the drill.


Since this is a work in progress, I have stripped down the comments and license (I’m keeping the same MIT license). That doesn’t mean I am trying to negate Phoenard’s work, or take undue credit. I need the code to be as compact as possible, and don’t need reams of comments to wade through, in every file. In due time, I will make a proper documentation for all the widgets, and the main M5Widget class, from which all widgets are descended.

The original is much more complex, and evolved. There are quite a few abstraction classes (widget container, display, etc), that I decided to skip to make things simpler. I have added one widget of my own, the M5Gauge, and will add more, as the need arises. Also, I will try to rework some of the isTouched logic into an abstraction class for the three buttons the M5Stack has. This would enable things like a scrollable ItemList, where Button A would be up, Button C down, and Button B select.


M5Stack Review

Core.pngI’ve been looking forward to playing with an M5Stack for a few months – since it first came out, really (or at least since I became aware of this project). I have been interested in LoRa for a while, having a specific use for this technology in mind. At the same time, I need a self-contained solution: playing with “nude” platforms is fine when you’re not demoing a concept. However, when you meet potential investors and they see PCB and wires, or, worse, a Tupperware™ box, they tend to be a wee bit dubious.

So a clean, extendable [stackable, hence the name] design, with a nice case, a screen, and, added recently, a LoRa module and a 8,500 mAh battery? Gimme! By the time I had made up my mind, there were three core models out. The newer models have an MPU-9250 sensor, which is not something I need right now. There an extension that transforms the M5Stack into a Calculator, Gameboy, Micropython micro-computer called (quite an unfortunate name if you ask me) Faces. Looks like a cool product too. I have however little need for Micropython (I have one of the original PyBoard Micropython boards, which I supported on KickStarter if memory serves, but for what I need, Python isn’t good enough. I’ll stay a little closer to the iron…), I might, one day, buy one, and rip the Micropython firmware out, if I can figure out a way to use the keyboards (the Gameboy and the calculator keyboards are the most attractive for my use).

So finally I made up my mind and bought set of the original core, the LoRa module, and the battery module. They have two online stores [that I know of], one on AliExpress, and one on Taobao. Considering that:

  1. I not only live in HK, but I can see Shenzhen from my windows.
  2. I can have stuff shipped to a Shenzhen address.
  3. Taobao was 32% cheaper.
  4. I can read and write Chinese.

I placed my order with Taobao, for a whopping 336 CNY, 52 USD. The order was processed and sent within a couple of days, and I got it today morning. It came by SF Express, not just any 快递. And the goods were well packaged in a small envelope. Compare with a NodeMCU board:


Oooooh, tiny.

Inside, each module is in its own sturdy plastic box. I’ll have some comments and suggestion about these, actually. Let’s have a look-see first:


So far so good. The boxes are sturdy, and deserve to be kept for later use. Inside, we have the modules, a USB cable (which gave me a lot of headaches – it didn’t work, and had me believe the VCP drivers didn’t work, grrr), some cables for prototyping, some stickers (including stickers for the three buttons), and two leaflets: a one-page Quick Start guide, and a longer, folder guide.


There are you issues with these leaflets. One, as I’m not a young man anymore, with tired eyes, they’re VERY hard to read. Ouchies. Two, some of the information is outdated. For instance, they give putStr() as a function to draw a string on the LCD. No workee. I went through the M5 library‘s header files (who doesn’t like to read header files, right?), and drawString() is what you need. Well, *I* need. The buttom functions are wrong too. The idea of a cheatsheet is great, and I’ll produce a (legible) one in a few days, once I’m done reading the header files. [EDIT: Done.]

But besides that, the leaflets are generally helpful. It helped also that I had been salivating about the M5 for a few weeks – I had read everything I could, I had installed the library. I was ready! 🙂

Suggestion #1

So what would be more helpful in terms of packaging is not a wider box, but a taller one. If the box for the Core was the same square as for the modules, but 3~5 times taller, we could put an assembled stack inside the box and close it. Whether for transport or for outdoors operation, it would be ideal.

Suggestion #2

Make a better leaflet. Or dispense with most of it. Put a QR Code (who types links, bro?) to your site, and please please please, steal my cheat sheet and put it up on your website (hint: start putting everything on github).

I have been working with Arduino and other IoT devices for a few years. I have all the drivers installed. On the M5 Forum and elsewhere, there have been complaints that upload doesn’t always work, especially on the older models. The current solutions are getting an older version of the SiLabs VCP drivers, or put a 2.2µf cap between ground and reset. The pins are accessible from the outside, so you have this cap sticking out of the case. A little unseemly, but that only when you need to upload. An ingenious Japanese user even soldered a cap inside the case, making it permanent and invisible.

When I plugged in the M5, nothing was detected. Hmmm. Tried with four Macs, two MacBook Air models, two MacBook Pro models, nada. Version 5, then downgrade to version 4, nada. Grrrr. I mentioned this, and the guys at M5Stack were responsive – but there was so much that they could do online of course… Usually the problem was with uploading, not the USB port per se. Then the M5 turned itself off: it wasn’t charging. Oops. Turns out, the Type-C cable, which was way too short, and would have gone to the trash anyway, was bad. One trip to Miniso later, and I was back on track.


The Factory Test. Yes, I hit buttons 3 and 1 twice. CACA. I’m childish.

After that all went well. I have been working with LoRa, LCDs and other stuff long enough to make everything work in literally minutes. I did have to poke around the header files, as I said (and you have to use M5.Lcd, instead of the more generic SSD1306 code I have already installed, for my TTGO boards. With a different syntax. Grrr. But all in all, it took me an hour to (re)make a LoRa sender and receiver for testing. I started with a sender, attaching a BMP280 barometer sensor to the Grove port [I2C port]. The M5 started pumping out Temperature and Pressure data in a JSON packet, which my TTGO machines happily received and decoded. Bueno.

Suggestion #3

A better USB cable. and not 10 cm long, kkthxbai.

20180113_160242_s.jpgNow, since the M5 is self-contained, and I have a nice battery plugged in, that’s the machine I’m going to take around my neighbourhood, to test the range. So I put a simple sender sketch in one of my TTGO boards, sending every ten seconds a JSON packet with just a PING, UUID, and sendCount as data. Should be good enough for a test. Later on, when I start working on my mesh, I’ll buy more M5, which I’ll put in various places in my ‘hood that I know are our of range (at least for the weaker TTGO). I can only count on people’s honesty to leave them alone and not run away with them! 🙂

I wish I had included the GPS module in my order, as I could use it to plot geolocations while doing the testing. I have a Grove GPS module from SeeedStudio, which I can probably use in between. I don’t fancy the idea of having a cable and small board dangling out, but, oh well…

Uh oh, no. I forgot that on the ESP32 the SoftwareSerial library isn’t [officially] available, so using the I2C port for this won’t work. Which brings me to one of my gripes: there’s space on the frame for another Grover port (or two, if one wants to be greedy). Having at least a port for Serial2 would be nice… I know all the pins are exposed on the base, but there’s gotta be a reason they used a Grove port for I2C (hint: because it’s friggin’ convenient).

I connected my SeeedStudio Grove GPS module on the base pins. The M5 started making a whistling sound – and not the man-seeing-a-pretty-girl kind of whistling. More like, some things inside the machine are not enjoying being interconnected-kind of death threat whistling. After a while it subsided, and only came back intermittently. Grrr. The base pins connections seem to produce a lot of noise, physical and electrical: I was getting a lot of garbled text, and wiggling the cables fixes it, sometimes only temporarily. There seems to be room from improvement here (and more argument for another Grove connector).

I uploaded sample code from Mikal Hart’s TinyGPS library, modified to make good use of the M5’s screen, and we were in business.

So my next order will have to include a GPS module then. I really like the idea of being able to able to do logging on the SD Card of GPS coordinates when doing LoRa tests. THAT brings me to two problems, one of which I might be able to fix, the other one not easily.

(1) Unless there is something wrong with *my* LoRa module (as opposed with the M5Stack LoRa module in general), the range is super bleh. I suspect[ed] the internal antenna (and tests show that it might be part of it), but I am getting ranges of 200 meters max, as opposed to 1.2 ~ 1.5 km with my small TTGO machines. Not cool. The first thing I did was disconnect the provided antenna, and use one of the spare antennas I had for my TTGOs. That “improved” the range to about 200 meters. Not exactly what I wanted. So I’ll do further testing, and maybe if I have time, and they’re willing, I’ll go see the guys at @M5Stack to see if we can look into it…


The LoRa module with its original antenna, disconnected, and the new one.

Suggestion #4

Let’s work on the LoRa module. I’m volunteering to go and see you guys, and help work on a solution. As it is now, your LoRa module (either the specific one I have, or in general) is useless.

Another problem is a software one, this time. There’s a convenient display-related command, M5.Lcd.drawJpgFile [which, I realize now, isn’t in the cheatsheet. Will fix that in a moment!]. The main issue is that the ESP32 stack uses TJpgDec, which is a nice tiny library. This library however isn’t too refined, and borks on some images. Consider this use-case, which I had in mind for my geoloc LoRa testing:

  1. Test LoRa reception.
  2. Acquire GPS coordinates.
  3. Save RSSI and GPS coordinates to SD Card.
  4. Call up Google Maps to get a static map as JPEG.
  5. Save file and display on-screen.

Which I tried. Nnngggggnnnn. It worked very well up to saving the data to the SD card. But displaying the map doesn’t work. Turns out the library borks on Google Maps JFIF images. I’ve tried to use the ArduinoJpeg library instead, but there are incompatibilities, and I need to have a look at the code first.

I might also have a look at GIF decoders — they are usually lightweight — as Google Static Maps can be requested in JPEG, PNG and GIF formats. Another project for later…

While writing this (long!) review, I wrote a bunch of sample sketches, to see what this machine can do. Apart from LoRa, which so far is a disappointment (or let’s call it a challenge…), everything else works well. One of the main things I noticed is that the M5Stack.h header makes my life (and my code) simpler. All this initialization crap you have to write in an Arduino sketch to setup SPI, Wire, SD, the TFT screen? No need. It’s done for you. Calling M5.begin(); is about everything you need.

There is, however, a catch. Custom libraries that require the SD library, for instance (or SD_Anywhere, which is usually my favored flavor for Arduino projects), will barf on your shoes. They call #include , or #include , and that creates incompatibilities. It solved it by adding in the libraries that call incompatible stuff this code:

#ifndef _M5STACK_H_
  #include <SD_Anywhere.h>
  // If we're using an M5, don't include outside SD libraries.
  #include <M5Stack.h>

Likewise, libraries that load UTFT or other TFT-specific libraries have to be shorted, BZZERT, so that they don’t mess up with your code. Remember, the M5 uses an ILI9341, which is pretty standard, and has a long list of features. And having a specific driver for YOUR screen (it’s not like you’re going to remove the ILI9341 and graft a new one…) saves space and time.

There’s also a lot that is not entirely exposed [at least in a obvious manner] by the M5Stack library. You have to look one floor down, in the ESP32 toolchain. For instance, you can set up the ESP32’s clock via SNTP. Does one really have to code all this by hand? Nah… Espressif has sample code to use LwIP SNTP. I was able to cut down a little on their code and make it more, well, M5Stacky, in a sample app that is growing beyond the reasonable… 🙂


I need to play more with that thing (and possibly buy a few more), but so far here’s my assessment.

The good stuff

  • Most of what’s in the box “just works”. Old farts like me remember Apple & Microsoft raving about plug’n’play. Well then. Once the USB issue was solved, plug and play indeed.
  • Bright screen. Generally good-quality build of the Core.
  • Good central library that simplifies coding.
  • Stacker design is a good idea, and the 2×15 pin + breadboard design is clever.
  • Pins exposed on all four sides of the base is a good use of real estate. Remember, that thing is tiny.
  • The three buttons. Good enough for many basic UI uses.
  • The I2C Grove connector.

The less-good stuff

  • Hit and miss stuff like crappy USB cable, LoRa reception, outdated info on a microscopic leaflet with printed URLs.
  • Slightly flimsy build of the modules. I’m always fearful of breaking them when dismantling the stack.
  • Whistling sounds when using the pins on the base.
  • No Serial2 Grove connector.
  • AFAIK, no way of turning off the M5Stack when charging it.
  • Less than obvious “double-click” gesture to turn it off (instead of more obvious long-press).
  • The company (or probably the single dude behind it) is not responsive, either on the M5Stack community or Twitter. Most questions stay unanswered, and posts on Twitter get liked/retweeted, but that’s it. I dunno if it’s a language issue, or El Jeffe being overworked, or what, but this has to change.

I’m quite happy, but still grumpy. I look forward to improvements, and will try to support where and when I can.

Basic BLE Functionality on the M5Stack

I am nowhere near a BLE expert – I have difficulties wrapping my mind around that thing, and so far regular BLE has been good enough for me. But ESP32 have BLE, and I had a use case for Bluetooth. So might as well try…


I have written, as a half joke, half proof of concept, a small app on the M5Stack that shows the temperature – it was so cold in the bus, with the A/C turned on in winter. As what I had on me was a BMP 280, it provides also atmospheric pressure, and altitude. But to calculate vaguely accurate altitude you need to have your locale’s sea level pressure. Which isn’t exactly convenient, when running this on a keyboard-less system. I had to change the seaLevel variable, recompile, and re-upload. No no no.

So, not entirely keyboard-less, as it has three buttons. The first solution was do provide 0.10 increment and decrement, with buttons A & C (left and right).

void buttons_test() {
  if (M5.BtnA.wasPressed()) {
    Serial.printf("-0.10 HPa");
    seaLevel -= 0.1;
  if (M5.BtnB.wasPressed()) {
  if (M5.BtnC.wasPressed()) {
    Serial.printf("+0.10 HPa");
    seaLevel += 0.1;

This works well enough. But it feels a little bit clunky. I could provide a small WiFi server, of course, to accepts POST or GET requests. But what about BLE?

The ESP32 examples have a small BLE_uart sketch that fits my purpose:

#define SERVICE_UUID           "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"

class MyServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) {
      deviceConnected = true;

    void onDisconnect(BLEServer* pServer) {
      deviceConnected = false;

class MyCallbacks: public BLECharacteristicCallbacks {
    void onWrite(BLECharacteristic *pCharacteristic) {
      std::string rxValue = pCharacteristic->getValue();
      double fValue;
      if (rxValue.length() > 0) {
        Serial.print("Received Value: ");
        for (int i = 0; i < rxValue.length(); i++)           Serial.print(rxValue[i]);         Serial.println();         Serial.println("*********");         fValue = ::atof(rxValue.c_str());         if (fValue > 0.0) {
          seaLevel = fValue;
          Serial.println("Changed seaLevel to " + String(seaLevel));


void setup() {


  // Create the BLE Device
  BLEDevice::init("BMP Sea Level Service");
  // Create the BLE Server
  BLEServer *pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());
  // Create the BLE Service
  BLEService *pService = pServer->createService(SERVICE_UUID);
  // Create a BLE Characteristic
  pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_TX, BLECharacteristic::PROPERTY_NOTIFY);
  pCharacteristic->addDescriptor(new BLE2902());
  BLECharacteristic *pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_RX, BLECharacteristic::PROPERTY_WRITE);
  pCharacteristic->setCallbacks(new MyCallbacks());
  // Start the service
  // Start advertising
  Serial.println("Waiting a client connection to notify...");

It does come at a price: the BLE stack is huge, and compiling the script takes a while, and the end-product ends up taking 75% of the Flash memory:

Screen Shot 2018-01-17 at 11.36.34 AM.png

But it works! If I look up the sea-level pressure on Thailand Meteo Dept’s web site, I can open a BLE scanner, connect to the M5, input the new sea-level pressure, and update.

I feel so much nerdy now 🙂

I’ve done this before in another project: the ultimate nerd way would be to look up automatically the relevant Meteo page, extract the sea-level pressure, and update it. So technically, I could have the M5Stack update itself, if it has a network. But, for the moment, this will do!

Full code on github.

Vroom Vroom

In Nitrous for the Arduino, I explained how to speed up code execution on Arduino machines, by replacing -Os directives with -Ofast. The procedure is slightly tedious, and every time I install a new version of the IDE, I have to set up things again. No more…

Screen Shot 2018-01-12 at 1.09.40 PM.png

In the background a sketch, compiled with the regular -Os directives. Code is 6,558 bytes. In the foreground, a small app I just wrote, offering me to change that. Let’s change that to -Ofast.

Screen Shot 2018-01-12 at 1.10.12 PM.png

Gasp! 14,400 bytes. That thing more than doubled in size. But is it effective? On a SeeedStudio Lotus, the sample test sketch of ricmoo’s QRCode library executes in 870 ms and 696 ms respectively. A 20% speed increase. Not bad…


The tool is a bit crude (hey, took me 30 minutes to write it!) and only recognizes the arduino/avr platform. What I need to do now is to make it a weeeeeee bit more versatile, looking for all platform.txt files. And maybe add more options. Oh, and, by the way, if you’re wondering whether this plain-text QR code works:

Hello World QR.jpg


Oy Vey, here’s a picchuh!

So, as I said in the previous instalment, once I had the data transfer from the OV528 sped up, I wanted to actually GET the photo displayed on my phone. I’m on Android (while I’ve been using Apple computers since 1987, I hate iPhones), and I really don’t like Java. So when I need a phone app, it’s Hello Phonegap.

In this case, it worked out quite well for me, as I had plenty of reusable code based on Don Coleman’s Bluetooth Serial plugin. The app lists the paired devices, and you choose the one you want. Bagus, not hard. The issue was how to display the image. Since the Phonegap framework is basically HTML/CSS/JS on top of a Java interface, I thought the easiest would be to have an element, and I could inline the data in Base64 format.

For this, I used the ESP8266-base64 library – there are many such libraries, and this ones works well enough. Since I’m getting the data in chunks, I sized my chunks so that they would be multiples of 3 [513 bytes] and every time I get a chunk of data, I add it to the file on the SD card, then Base64-encode it and send that over Bluetooth to the app:

 void GetData() {
  Serial.print(F("Setting baud to 115200..."));
  setBaud(BPS115200_A, BPS115200_B);
  Serial.println(F(" done."));

  // PIC_PKT_LEN = 519 [513 + 6 for overhead]
  unsigned int pktCnt = (picTotalLen) / (PIC_PKT_LEN - 6);
  if ((picTotalLen % (PIC_PKT_LEN - 6)) != 0) pktCnt += 1;
  char cmd[] = { 0xaa, 0x0e | cameraAddr, 0x00, 0x00, 0x00, 0x00 };
  char pkt[PIC_PKT_LEN];
  char picName[] = "pic00.jpg";
  picName[3] = picNameNum / 10 + '0';
  picName[4] = picNameNum % 10 + '0';
  if (SD.exists(picName)) {
  myFile = SD.open(picName, FILE_WRITE);
  if (!myFile) {
    Serial.println(F("myFile open fail..."));
  } else {
    for (unsigned int i = 0; i < pktCnt; i++) {
      cmd[4] = i & 0xff;
      cmd[5] = (i >> 8) & 0xff;
      int retry_cnt = 0;
      sendCmd(cmd, 6);
      uint16_t cnt = CAM_SERIAL.readBytes((char *)pkt, PIC_PKT_LEN);
      unsigned char sum = 0;
      for (int y = 0; y < cnt - 2; y++) {
        sum += pkt[y];
      if (sum != pkt[cnt - 2]) {
        if (++retry_cnt< 100) goto retry;
        else break;
      myFile.write((const uint8_t *)&pkt[4], cnt - 6);
      char b64data[684]; // 4/3 of 513
      int b64len = base64_encode(b64data, &pkt[4], cnt - 6);
      BT_SERIAL.write((const uint8_t *)&b64data[0], b64len);
    cmd[4] = 0xf0;
    cmd[5] = 0xf0;
    sendCmd(cmd, 6);
  Serial.print(F("Setting baud to 9600..."));
  setBaud(BPS9600_A, BPS9600_B);
  Serial.println(F(" done."));

A ‘\n’ char is sent at the end, as in the JS code, an event is subscribed to on receiving a ‘\n’ char:

bluetoothSerial.subscribe('\n', app.onData, app.onError); 

And in “app.onData” I set the “src” of the image to the value received:

 if(data.indexOf("data:image")>-1) {
} else {
  console.log("weird data");

This was probably the easiest and most elegant solution. Preformat the picture so that it can be displayed right away. No fiddling with files.

Next step: sending options to the Arduino, like picture size (the OV528 has 7 possible sizes), file name for the SD card, etc. Since Javascript does JSON natively, and there’s an ArduinoJson library, it should be too hard 🙂

Speeding up the OV528

Coding is mostly a hobby for me (although for a few years it did pay a large share of the bills), so when something doesn’t work properly, I don’t always have the time, patience, resilience to fix it right away – or sometimes for a long time. But then, episodically, I have some coding fits, and everything falls in place. I’ve been having a few of those lately. For instance, I’ve been postponing the Matasano Challenges for a long while. I got stuck at some point, and couldn’t be arsed. And then, a week or two ago, I completed set 1, and started set 2, in one language (Xojo) and rewrote most of set 1 in Java. From scratch. Took me two days. I think I remembered to eat, but that’s about it.

Which brings us to this post – I know, I tend to digress. I’ve had an OV528 Grove Serial Camera from SeeedStudio for a few years, and while it worked okay, having to use it at 9,600 bauds was painfully slow. I didn’t have time to experiment, and let it go. It was too slow for my use case. Then in my coding rage, I thought I’d give it another shot.

So the first thing was to find how to change the baud rate. Which proved slightly hairy. The manual says:

5. Set Baud Rate (AA07h)

Host issues this command to set camera’s baud rate. Camera auto-detects the baud rate issued by host and keep the baud rate to communicate with host, reconfiguration should be made if power off. The baud rates were supported by camera as follows.

The command:

Screen Shot 2018-01-10 at 1.04.52 AM.png

The baud rates [dividers]:

Screen Shot 2018-01-10 at 1.03.02 AM.png

So if you want to set the baud rate to 115,200, you need to send this command:

0xaa, 0x07, 0x0F, 0x01, 0, 0

You then read the response and hope for the best. One of the manuals for the OV528 I have says “Camera reply in the baud rate which was not changed”. Which I figured out to mean that the camera replies at the original baud rate (9,600 normally), and then only, sets itself to the new rate.

So far so good. So I created a method to set the baud rate.

#define BPS9600_A 0xBF
#define BPS9600_B 0x01
#define BPS19200_A 0x5F
#define BPS19200_B 0x01
#define BPS38400_A 0x2F
#define BPS38400_B 0x01
#define BPS57600_A 0x1F
#define BPS57600_B 0x01
#define BPS115200_A 0x0F
#define BPS115200_B 0x01

void setBaud(char BPS_A, char BPS_B) {
char cmd[] = { 0xaa, 0x07, BPS_A, BPS_B, 0, 0};
unsigned char resp[6];
while (1) {
sendCmd(cmd, 6);
uint8_t x = CAM_SERIAL.readBytes((char *)resp, 6);
if (x != 6) continue;
if (resp[0] == 0xaa && resp[1] == 0x0e && resp[2] == 0x07 && resp[4] == 0 && resp[5] == 0) {

After calling this method, you have to reset the Serial port too. I use a Mega, and the camera is on Serial2 (Serial1 is for the Bluebooth module, to transfer the photo). So resetting the baud rate looks like:

setBaud(BPS115200_A, BPS115200_B);

I called the method in the setup, after initializing the camera. The problem was that the camera wasn’t ready for it. Turns out, you have to do it just before the data transfer stage. All commands have to be passed at 9,600. After playing around a while, I ended up setting the baud rate to 115,200 just before transfer, and back to 9,600 when done.

This worked well. The photo transfer was super fast. Twelve times, give or take [115,200 / 9,600 = 12 after all!]. As I said, I use two pipes: one for the Mega to communicate with the camera, and one for the Mega to communicate with the outside world – in the present case a bluetooth module and my computer on the other end. The Mega gets the data in 512-byte chunks, which are saved in a JPEG file, and sent to Serial1. At the end of the transfer, the file is closed, and the JPEGDecoder library‘s renderJPEG function is called to display the photo.

So I have two windows, Serial in the Arduino IDE, and Serial1/Bluetooth in CoolTerm. The Bluetooth module works at 38,400 bauds, so the transfer to the computer is slower – I need to set the baud rate there to 115,200 to make things smooth. But so far it is good enough. I get a 320×240 photo in less than 2 seconds.

The next step is to write a Phonegap app to interact with the Mega, taking pictures and displaying them on the phone.


Done 🙂 Let’s make that another post.

Experimenting with LoRa+ESP32

With the launch of ESP32, an upgraded version of ESP8266, and the popularization of LoRa (and popular means cheap[er]!), I have started playing with LoRa, having bought a bunch of integrated ESP32+LoRa+OLED machines. They’re cheap enough, and provide a good enough package to start experimenting.

Each two-piece set comes loaded with basic sender/receiver sketches. You turn them on, and they start pinging each other. That was enough to test range. I live in a heavily built area, with lots of 50+ storey buildings. A nice urban environment to try LoRa communication. Walking around in my neighbourhood, widening the circle, I managed to get firm pings from the machine I had left home, right in front of the bay window, from up to 1.1~1.2 kilometers. I mapped places on Google Maps where I got pings, using the distance tool to measure the range. Not exceptional range, but considering the crowded environment of my neighbourhood, I’d say not bad.

So after walking a few kilometers (who said nerds don’t exercice?), I went back home and started coding. But first I had to install some libraries. On the AliExpress/LilyGo page, they give this link, which took forever to download. The package is mostly for Windows, so, being on a Mac, I copied the libraries I needed (mostly LoRa and OLED) to the ESP32 hardware folder.



So these boards are programmed just as any ESP32 boards. The LoRa and OLED libraries have examples that can be used right out of the box. However, what’s disappointing is that the LoRa receiver/sender examples are Serial only, without OLED – like the preloaded sketches. So I rewrote first the examples so that they display what’s happening on the OLED screen, without  the need for a Serial connection. The basics are:

#include "SSD1306.h" // alias for #include "SSD1306Wire.h"
SSD1306 display(0x3c, 4, 15);
void setup() {
  display.drawString(0, 0, "LoRa Sender");
  // This last line of code is very important.
  //Without it nothing gets displayed.

There isn’t much space on the OLED, so I am using it sparingly — but showing “msg #X sent” and the local IP address is good enough for now. But sending dumb automatic messages is boring, so I spiced things up a little. First, in order to make message parsing easier (since I have a more complex use case in mind than just sending HELLO), I integrated the ArduinoJson library to the project. So now we’re sending easily parsable messages, effortlessly. The main code looks like this:

void sndMsg(String myMsg, String myName) {
  Serial.print("Sending packet: ");
  StaticJsonBuffer<256> jsonBuffer;
  JsonObject& root = jsonBuffer.createObject();
  root["from"] = myName;
  root["msg"] = myMsg;
  root["sendCount"] = counter;
  char buffer[256];
  root.printTo(buffer, sizeof(buffer));
  // send packet
  display.fillRect(0, 16, 128, 48);
  display.drawString(0, 16, "sent msg #" + String(counter) + " from " + myName);
  display.drawString(0, 28, myMsg);

Note that this code is fragile. The static JSON buffer, with a fixed size of 256 bytes, could bork on larger messages. In a real application it would be better to use:
DynamicJsonBuffer jsonBuffer(bufferSize)
where bufferSize is declared as:
const size_t bufferSize = JSON_OBJECT_SIZE(Y) + XXX

Where Y XXX is:

  • The sum of the lengths of keys and values.
  • 2 for the {} curly braces.
  • 6 for each key/value pair: “…”:”…”, Two pairs of quotes, a colon, a comma.

The actual count can be more complicated than that, and is clearly explained in Memory Model. Keep in mind also that the overhead varies from platform to platform. Eight-bit platforms are indeed more economical, but I’d rather have a slightly greedier implementation on an ESP32, than on a Spartan one on an Arduino Uno 🙂

Anyway, for now a static buffer will do, knowing I’m the only user and know not to abuse the system 🙂

This of course implies now that the receiver code has to be updated to parse JSON and display the bits we want: from, sendCount, msg. Parsing JSON is just as easy:

void loop() {
  // try to parse packet
  int packetSize = LoRa.parsePacket();
  if (packetSize) {
    // try to parse packet
    // received a packet
    Serial.println("Received packet:");
    // read packet
    ix = 0;
    output = "";
    while (LoRa.available()) {
      char c = LoRa.read();
      buff[ix++] = c;
      output += (char)c;
    buff[ix++] = '\0';
    StaticJsonBuffer<200> jsonBuffer;
    JsonObject& root = jsonBuffer.parseObject(buff);
    if (!root.success()) {
      Serial.println("parseObject() failed");
    const char* from = root["from"];
    const char* msg = root["msg"];
    const char* sendCount = root["sendCount"];
    // Print values.
    Serial.print("from: ");
    Serial.print("msg: ");
    display.drawString(0, 0, "Receiver");
    display.drawString(0, 26, "msg: ");
    display.drawString(0, 40, "send count: ");
    // print RSSI of packet
    Serial.print("RSSI: ");
    if (strcmp(msg, "PING") == 0) {
      const char* IP = root["IP"];
      Serial.print("IP: ");
      display.drawString(0, 50, "IP: " + String(IP));
    display.drawString(0, 18, from);
    display.drawString(128, 0, String(LoRa.packetRssi()));
    display.drawString(128, 30, msg);
    display.drawString(128, 40, sendCount);

What’s next?

The main idea behind this experimenting is to have a network / mesh of very small LoRa boards, each paired with a mobile phone or a laptop [via BLE or WiFi], giving the phones and laptops LoRa capability. The gateway would be an M5Stack, for the moment (I just bought one, need to try it out). Probably something more powerful later. I happen to see Shenzhen from my windows, and with a powerful gateway, I might be able to do a few things:

  • Jump China’s Great Firewall when needed by using the Hong Kong gateway as a proxy server.
  • Monitor my home – I already have a few pieces of hardware up and running: BMP180 in my wine fridge running on an Onion Omega2, OV528 Camera, PIR Motion Sensor, etc…
  • [Group] chat app, away from prying eyes – one reason WhatsApp is not working anymore in China is because the government can’t spy on you. Well then. The Arduino AESLib works well, albeit limited to AES128. And I could always port the Rijndael C implementation to ESP32. Probably effortlessly.

These are some of the ideas I have. I’m sure I can work out a few more. In between, happy LoRa-ing!