mxiot is a low-cost hardware prototyping platform to help you explore switching small IoT projects from bare-metal to a secure-boot-capable WiFi/BT-connected Embedded Linux system capable of running rudimentary C/C++, as well as applications written in almost any modern language or application framework (like Qt, Rust, Ruby, Python, Node.js, or .NET Core).
The heart of mxiot is the i.MX6ULZ, a 900 MHz Cortex-A7 microprocessor available direct from NXP for $2.68 (even in single quantities). These are drop-in compatible with the i.MX6UL and i.MX6ULL.
The i.MX6ULZ is stripped down to the basics, but there's plenty there for most always-on IoT projects. The board exposes:
- 3 UARTs (one with hardware flow-control signals, and one dedicated to the console)
- Two I2C peripherals (one shared with one of the UARTs)
- SPI with 2 chip-select signals
- I2S with separate BCLK/WS signals for TX and RX paths.
- 4x PWM outputs
- Two USB ports
In addition to the DRAM and SD Card socket necessary for booting, mxiot also sports a small complement of on-board peripherals:
- WiFi and Bluetooth connectivity
- 4x 12-bit analog inputs, care of the Texas Instruments TLA2024
- APA102 addressable RGB LED
- QSPI Flash (that you can optionally boot from)
- Pushbutton
WiFi and BLE are handled by whatever 44-pin standard SDIO-interfaced WiFi/BT module du jour you'd like to populate; the board supports all physically-compatible Ampak modules, as well as extremely low-cost alternatives, like the RTL8723BS modules regularly found for around $2 from AliExpress, Taobao, or other distributors in Asia.
The design is open-source, and more importantly, the BOM has wide availability and doesn't lean on ICs with limited documentation or support. This means you can prototype your project around the mxiot and then copy the guts into your own design to optimize for size, power consumption, cost, or expanded functionality.
You can download a ZIP of the repo and upload this file directly to Altium's online viewer to interact with the PCB, schematic, BOM, and 3D model of the design without having to download any software. The repo also contains a schematic and gerbers for offline viewing.
For the sake of compactness, mxiot tightly arranges parts onto a dense 6-layer PCB measuring only 2.3 x 0.8 inches (58 x 20mm). Having said that, I've done many lower-cost 4-layer designs with mostly single-sided placements around the same guts of this board without any routing issues.
I'm a hardware guy, not a cybersecurity consultant, so I know that it's really easy to roll your eyes when people start talking about secure-boot and key storage. But seriously, folks, if you are making devices that you're going to sell to people that will connect to the internet, you have an ethical obligation to think through the security implications. You should be prototyping around hardware that has the underlying capabilities that allow you to turn on security measures during the design cycle.
The i.MX6's OTP key storage, TrustZone, and secure-boot capabilities let you establish a chain of trust from the boot ROM, to U-Boot, to the kernel, and an encrypted rootfs, which will allow you to store private data (like keys!) without having to worry as much about physical device security. You can also transport firmware updates over unencrypted channels and not have to worry about your image being reverse-engineered or modified.
- ...the Raspberry Pi Zero? While it has plenty of DRAM, the older ARM11 processor is incompatible with newer application frameworks. There's also no pathway to using the processor in a custom design, and no secure boot capability.
- ...the Allwinner V3s or F1C100s SIPs? These are fine for C/C++ applications or very simple Python scripts, but the V3s lacks sufficient RAM to run large, modern application frameworks, and the F1C200s is an older ARM9 cores that have dwindling software support and no security features.
- ...the Microchip SAMA5D27 or SAM9X60 SIPs? These both cost twice as much as the combined cost of the i.MX6ULZ and 512MB DDR3, while offering inferior performance as well as the software compatibility issues mentioned above.
- ...the OSD32MP15x or OSD335x modules? Unless you're literally assembling no more than a handful of units for the entirety of the project's lifecycle, these are way too expensive to justify the time savings when compared to an iMX6UL/ULL/ULZ design.
Grab the mxiot schematics as a starting point, but solder down a full-featured i.MX6ULL. These come in tons of different SKUs that mix and match peripherals, operating frequencies, and temperature grades. All of these are 100% electrically and mechanically compatible (in fact, mxiot started out as a 528 MHz i.MX6UL dev board from 5 years ago!).
OSHPark's test 6-layer service and JLC's 6-layer service have both been tested with the gerber files and the resulting boards pass stress-tests. While JLC's stack-up isn't ideal for the impedance targets of the design, it is much less expensive than a full-custom stack-up from a different board house. The boards will cost roughly $100 when ordered with ENIG (highly recommended). I also recommend ordering an electropolished solder stencil. I plan to do some EMC chamber testing at some point to see if the impedance mismatches on the JLC boards cause more-pronounced emissions.
mxiot is designed to be hand-assembled with a kitchen hotplate and low-cost hot-air gun. To assemble, paste up the top side of the PCB, place the components, and heat with a hot plate until the solder becomes molten. You can ever-so-slightly tap the BGAs on the board and they should bounce back (due to the surface tension in the solder). Pay special attention to DRAM alignment; there are a multitude of silkscreen lines that cover some, but not all, standard DDR3 footprints.
Now is a good time to test for short-circuits and open-disconnects. If you screw up a BGA placement, you can use hot air to remove the chip, then clean the pads, apply flux, and put a bare BGA (without additional paste) on the board and re-heat it on the hotplate until it passes the "poke test."
Next, flip the board over, paste up the bottom side, and place the remaining parts.
For being a WiFi/BT-enabled embedded Linux system, mxiot has an extremely terse BOM: fewer than 25 unique parts for an RTL8723-based configuration (and even fewer if you start removing extraneous peripherals). The mxiot design is much simpler than the reference EVK design, so I wanted to share some of the design decisions and trade-offs that went into this.
The i.MX6UL/ULL/ULZ has several internal regulators that — while not strictly staying within recommended operating conditions — allows the entire system to be powered by a 3.3V and ~1.35V rail (provided you're using DDR3L instead of 1.5V DDR3). Rather than a bulky, complex PMIC, mxiot uses two fixed-output MIC23150s to provide these rails. The 1.35V rail is somewhat-haphazardly sequenced to come on after the 3.3V rail.
This power supply topology is designed for always-on, wall-powered applications. The platform is incapable of doing any dynamic voltage-scaling, so power consumption could be significantly worse (depending on workload) than with a PMIC or adjustable regulator.
The i.MX6 has support for a multitude of a boot modes usually prototyped by GPIO strapping. Instead of exposing these signals (which would require tons of resistor switches), I rely on the SD/MMC Manufacture Mode, where the chip will always boot from MMC0 unless the OTP boot configuration memory is flashed. If you wish to boot from QSPI flash, you'll have to initially boot from MMC, and then program the OTP memory (hoping to get it right to avoid bricking your imx6).
Rather than use a discrete WiFi/BT solution, mxiot uses standard 44-pin SDIO/UART modules that help make small-volume hand-assembly faster (as these circuits tend to have many different passive values).
And where possible, internal pull-ups have been used (even on the POR reset pin). There are still a few places to optimize the BOM, but this will take further testing to validate.
- i.MX6ULZ
- DRAM stress tests
- RTL8723BS WiFi
- TLA2024 ADC
- USB port
- JTAG port
Not tested yet:
- RTL8723BS BT
- APA102 RGB LED
- Pushbutton
- QSPI Flash
While you could use mxiot as a (very bad) single-board computer, it's designed for embedded tasks. Most of the sensors and peripherals you'll use with this board will require kernel and DTS modifications; I recommend setting up a project-specific Buildroot tree you can work out of to build a U-Boot, Linux kernel, and rootfs with the specific packages you need.
I don't have published patches yet, but you can largely use an unmodified i.MX6ULZ EVK port to get mxiot to boot.
Buildroot doesn't have an i.MX6ULZ defconfig, but you can start with an i.MX6ULL one and go from there
mkdir mxiot && cd mxiot
git clone https://git.busybox.net/buildroot/
cd buildroot
make imx6ullevk_defconfig
make menuconfig
Change Kernel > In-tree Device Tree Source file names to imx6ulz-14x14-evk
Go to Bootloaders. Change Build system to Kconfig and Board defconfig to mx6ulz_14x14_evk
The big change you have to make is to switch SDHC2 to SDHC1 everywhere, since mxiot boots from the first MMC device (which is required to use SD/MMC Manufacture Mode), while the i.MX6UL, ULL, and ULZ EVKs all boot from SDHC2.
If you're in Buildroot, you can access the U-Boot menuconfig with make uboot-menuconfig
. Make sure to enable the FSL USDHC MMC driver (CONFIG_FSL_USDHC=y
) — I've noticed some defconfigs don't activate this driver by default. Also, change Environment > mmc device number (CONFIG_SYS_MMC_ENV_DEV
) to 0.
You'll have to hack at u-boot/include/configs/mx6ullevk.h
to switch the default SDHC2 boot device to SDHC1. These two changes should do the trick:
//#define CONFIG_SYS_FSL_ESDHC_ADDR USDHC2_BASE_ADDR
#define CONFIG_SYS_FSL_ESDHC_ADDR USDHC1_BASE_ADDR
...
//#define CONFIG_MMCROOT "/dev/mmcblk1p2"
#define CONFIG_MMCROOT "/dev/mmcblk0p2"
There are lots of changes to make here depending on what you want your board to do. If you just want to get to a login and poke around with userspace tools, the default kernel and device tree should work.
Otherwise, here are some notes to get the onboard hardware working:
To get WiFi working, assuming you've soldered down an RTL8723BS module, choose CONFIG_RTL8723BS=m
Bluetooth requires activating three-wire HCI support in the Linux kernel (BT_HCIUART_3WIRE
) and using the appropriate firmware and hciattach software available from lwfinger.
The 4 analog inputs are provided by an external I2C ADC, the TLA2024, which is on I2C2 at address 0x48. To bring this sensor up, edit your DTS (by default, linux/arch/arm/boot/dts/imx6ulz-14x14-evk.dts
) to include it:
&i2c2 {
...
tla2024: adc0@48 {
compatible = "ti,tla2024";
reg = <0x48>;
v0@0 {
single-channel = <0>;
};
v1@1 {
single-channel = <1>;
};
v2@2 {
single-channel = <2>;
};
v3@3 {
single-channel = <3>;
};
};
};
And apply this patch and activate the new driver.
Other than the two I2C buses, if you want to use any of the I/O pins for their peripheral functions, or get audio working, you'll have to enable the appropriate peripherals in your DTS file and/or enable specific drivers in the Linux kernel.