Program ESP32 without the Arduino IDE

I recently had to make a field upgrade to the software on some ESP32-based devices. I didn’t want to have to require that the operator go through the entire process of installing the Arduino IDE, download libraries, and all that not-so-fun stuff. For us techies, it’s no big deal. For a non-technical user who has tons of more important things to do, it becomes a royal pain in the butt.

So, what to do? From watching the IDE output, I could see that it called esptool.exe. A bit of research into this tool and it looked like the only problem we were likely to run into was figuring out which virtual COM port had been assigned to the device. Also, since there were a number of these units to reprogram, it needed to be efficient.

Enter the chgport command. chgport lets you change port settings, but for my purposes the most important part was that it lists the COM ports connected to the PC.

I could redirect the output from chgport to a file, then parse that file and supply esptool with the COM port of the device. After verifying this manually, I went forward and built this script.

set BOOT=PROGRAM.ino.bootloader.bin
set PART=PROGRAM.ino.partitions.bin  
set PROG=PROGRAM.ino.m5stack_core2.bin
set APP=boot_app0.bin

chgport > port.txt
set /p PORTLINE= < port.txt
set PORTNAME=%PORTLINE:~0,5%
echo %PORTNAME%

esptool.exe --chip esp32 --port %PORTNAME% --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size 16MB 0x1000 %BOOT% 0x8000 %PART% 0xe000 %APP% 0x10000 %PROG%
pause

The name of the actual file programmed into the M5Stack device was replaced above by “PROGRAM.ino” Telling the IDE to save the compiled binary will put this file, along with the bootloader and partition configuration into your current working directory.

Next, you can see that we redirect the output of chgport to a file, then set a variable PORTLINE to the content of that file. The next line strips out the first 5 characters and stores it in the PORTNAME variable. This imposes an important requirement for using this batch script: the Arduino device must be the only, or at least the first, COM port in the file. It’s possible to work around this limitation, but it would require a more complex batch file, or perhaps even a Python script or similar.

Assuming we can successfully get the COM port, the next line calls the esptool with the necessary parameters and the device is programmed. I added a “pause” command so the user can see the result of the script before the window disappears.

There you have it. HTH!




If you'd like to subscribe to this blog, please click here.

Ready to ship

I love it when a project comes together nicely.

M5 Stack has some really great hardware and made this project so much smoother. 12V input signal with local status displayed on screen and also communicated to App via BLE! All based on the M5Stack Tough units.




If you'd like to subscribe to this blog, please click here.

Arduino 10MR & 10MT PLC modules

I’ve been seeing these Arduino-based “PLCs” on the internet for a while. If you’re not familiar with PLCs, they are basically control computers with built-in or expandable Inputs and Outputs and are used for industrial control. They are typically hardened against accidental overload (too much current on the output or wrong polarity voltage on inputs) and run on 24VDC They’re built to work reliably in a harsh industrial environment. They are also usually quite expensive, although there are low-priced versions available, such as the Click PLC starting around $100 each.

There have been industrialized versions of Arduino-based controllers for years. I have built a few projects based on the Industruino, for example, and I quite like it.

But these new versions like the 10MT and the 10MR address the lower end of the market that doesn’t require that level of “toughness”. I was curious, so I picked up a couple. They’re cheap, right, so how can you go wrong?

10MR PLC

First impressions are that it looks pretty good. It’s mountable either by DIN rail or four screw holes in the feet. The material seems to be ABS plastic. Polycarbonate would have been nice. Programming is via a DE-9 serial port, so you will need to connect it to a serial port. A USB-to-serial converter should work just fine. The connections are by rising-cage screw terminals, which was a nice surprise. Most Arduino clone connectors use the super cheap and annoying terminals that can’t grip wire properly unless you use ferrules. I mean, these are cheap terminals, but not the bottom of the barrel. These actually work fairly well.

The module comes with four optically-isolated FET open-drain (aka “NPN”) power-driver outputs or relays, depending on the version you choose. Outputs are labelled Y0 through Y3 and are assigned to Arduino pins 9 – 12. The 10MR has relays and the 10MT has transistors. There are also 6 digital inputs and 3 analog inputs and a 2-line, 16 character LCD. The units specify a 24VDC power supply, but I have successfully run them from 12V also.

Overview

So far I’ve only used the outputs on the 10MR and aside from a momentary surprise — the outputs are fully isolated, so you need to supply a ground and power to the load, it worked immediately to drive my test solenoid. Since it’s Arduino powered, it’s easy to control using the standard Arduino API commands.

Having a display is a really nice plus, but it would be even better if there were a few pushbuttons. However, those are easy enough to add with the many digital inputs. I put together a demo platform with a power supply and a pushbutton mounted to a short piece of DIN rail and the PLC screwed directly to a panel. For output, I have a small stacklight that I converted from incandescent lighting to LED.

I ran a test with a basic one-shot timer having a pushbutton input on X0 and an output on Y0. Here’s the code.

// Project sponsor: N/A
// Email: sales@cedarlakeinstruments.com
// Creator: Cedar Lake Instruments LLC
// Date: January 2023
//
// Description:
// Demonstrate one-shot logic with Arduino
// Tested on ZanhorDuino board
// 
// Arduino pins
// 9 - Digital trigger input. Pull LOW to trigger one shot
// 8 - One shot output (goes HIGH for one shot time)
//
//
// Test program for ZanhorDuino PLC
// NPN outputs are optically isolated so need their own V+ & GND connections
//

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>

// set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x27,16,2);  

// ********** U S E R  A D J U S T M E N T S ********************
// One shot time in milliseconds
const int DELAY_MS = 6000;
// **************************************************************

const int INTERVAL_MS = 100;
int _timeout = 0;
const bool OUTPUT_ON = false;
const bool OUTPUT_OFF = true;

// X0 is input
const int TRIGGER_PIN = 8;

// Y0 is output
const int OUT_PIN = 9 ;


void setup() 
{
    pinMode(TRIGGER_PIN, INPUT_PULLUP);
    pinMode(OUT_PIN, OUTPUT);
    digitalWrite(OUT_PIN, OUTPUT_OFF);

    lcd.init();
    // Print a message to the LCD.
    lcd.backlight();
    printStatus(DELAY_MS, LOW);   
}

void loop() 
{
    // One shot triggers on high->low transition of TRIGGER pin
    while (digitalRead(TRIGGER_PIN) == HIGH)
    {
        // Hold while pin not pressed
    } 

    // Activate output and start timing
    _timeout = DELAY_MS;
    digitalWrite(OUT_PIN, OUTPUT_ON);   

    // Hold output High until timeout 
    while ((_timeout-= INTERVAL_MS) > 0)
    {
        printStatus(_timeout, HIGH);
        delay(INTERVAL_MS);
    }

    // Turn output off
    digitalWrite(OUT_PIN, OUTPUT_OFF);
    // Update display
    printStatus(DELAY_MS, LOW);
}

// Prints remaining time and output status
void printStatus(int t, bool state)
{
    char buffer[17];
    // Pad to 16 characters: 12 for text (left-justified) 
    // and 4 for time (right-justified)
    sprintf(buffer,"%-12s%4d","TIME:",t/1000);
    lcd.setCursor(0,0);
    lcd.print(buffer);
    
    lcd.setCursor(0,1);
    state ? lcd.print("Output ON ") : lcd.print("Output OFF");
}

This works nicely. Press the pushbutton and the output turns on and the display shows a countdown in seconds until the output is back off.

When I have some more time, I’ll take a look at the analog inputs!




If you'd like to subscribe to this blog, please click here.

A high(er) voltage DAC using PWM

Sometimes you just need to do things the old way. A recent post on the Arduino forum was asking about a method to replace the manual speed control potentiometer with something that could be automated. The need was to just replace the pot without replacing the entire control. Since the potentiometer provides a variable voltage to the control circuit, you’d normally use a DAC (Digital to Analog Converter) for this application. We don’t know what voltages may exist internally, so it’s a good idea to isolate the controller from the rest of the circuit. I decided that this could be a good application of a PWM (Pulse-Width Modulated) DAC.

By filtering a PWM signal, we can create a DC voltage. This has the advantage of being a very simple way to produce a variable DC output at low cost using the Arduino. We can use a basic optoisolator like a 4N26 to isolate the Arduino from the rest of the motor control circuit.

Here’s the circuit

This takes the PWM output from the Arduino at pin 11 and feeds it into an optoisolator through the resistor R1. This protects the Arduino from any high voltage in the motor controller. There approximately 1,000V of isolation between the input pins (1&2) and the output (4&5). R2 is a pullup resistor that gives us +15V at the output of the isolator. Next, we buffer the output of the isolator with a CMOS 4011 AND gate. This is to provide an even drive to the RC filter. By using a 4000-series CMOS gate, we can supply up to 18V output. This is a very old logic series, dating back to the 1970’s but the parts are cheap, easy to find and work pretty well in this kind of application. The main drawback to 4000 series CMOS is that it’s pretty slow, but that doesn’t affect us here.

The output of the NAND gate drives an RC filter (R3, C1) that provides a smooth DC value from the input PWM. J1 provides our 15V power input.

And the code to run it.

const int DAC_PIN = 11;

void setup() 
{
  // put your setup code here, to run once:
  pinMode(DAC_PIN, OUTPUT);
}

void loop() 
{
  // put your main code here, to run repeatedly:
  for (int i = 0; i < 256; i++)
  {
      analogWrite(DAC_PIN, i);
      delay(5);
  }
}

So you can see here a simple, but interesting method of creating a high voltage, isolated output from the standard Arduino pins.




If you'd like to subscribe to this blog, please click here.

A bit about pumps

Syringe pumps, valve pumps and peristaltic pumps are classified as positive-displacement pumps.

When a solenoid valve opens or closes, there is usually a small amount of liquid moved. This effect can be deliberately used as a pump. There are solenoid valves that are also specified for use as precision pumps, but they have to be at least 2-way (3-port) valves since you need a source to aspirate from and a destination to dispense into. They are very precise but you can’t control the volume of the dispense except by making multiple dispenses.

Syringe pumps as you know use the motion of a piston in a cylinder to aspirate or dispense a fluid. They can be extremely precise and accurate if built properly. I used to work with some that were accurate below 10 microliters and precise, IIRC to within about a microliter. However, they were also very expensive. Syringe pumps are generally slow moving as high speed can cause the pump to cavitate, which reduces precision and introduces air into the fluid path. Cavitation is a release of dissolved gas (air) into the pumped fluid when the fluid pressure drops below the vapor pressure of the gas. This causes the gas to be released as small bubbles.

One common technique that I saw when I was involved in the medical device industry is to build a motorized fixture that a syringe can fit into and and use that to control the piston. It gives a high precision and also solves the problem of contamination: just throw the syringe away when you’re done.

Peristaltic pumps are also an option for a positive-displacement pump, but they are typically an order of magnitude less accurate than syringe pumps. They have the advantage that material compatibility is mainly an issue of the what the tubing is made from.




If you'd like to subscribe to this blog, please click here.

DF1201S MP3 PlayerPro & Arduino

One of my uses for this blog to is to record useful information that I (and others) might need later.

I’ve used the DfRobot Player Pro a couple of times in “stand alone” mode. It’s a great audio player: small, cheap, and easy to use. It also has a serial interface so it can be controlled by an arduino or microcontroller. DF Robot provides the DFRobot_DS1201S library to make this easier. However, all the examples I see online are using the playFileNum(num) command that takes an index of a file (MP3) to be played. The problem with this is that the file index depends on the order in which the file file was stored to the Player Pro’s Flash memory.

There is also a playSpecFile(String) method that takes a filename, which you’d expect is much easier and would be more popular. However, when I tried using it, it always played the same song. OK, time to debug. There is also a method in their library to retrieve the name of the currently playing file. However, when I use code like this:

// Play file
DF1201S.playSpecFile("one.mp3");
// Print name of file that's playing
Serial.println(DF1201S.getFileName());
DF1201S.playSpecFile("two.mp3");
// Print name of file that's playing
Serial.println(DF1201S.getFileName());
DF1201S.playSpecFile("three.mp3");
// Print name of file that's playing
Serial.println(DF1201S.getFileName());

It always plays the same song and to boot, I get Chinese characters displayed for the file name. Clearly, not what I was trying to play.

However, make a minor change to the filename and this works.


// Play file
DF1201S.playSpecFile("/one.mp3");
// Print name of file that's playing
Serial.println(DF1201S.getFileName());
DF1201S.playSpecFile("/two.mp3");
// Print name of file that's playing
Serial.println(DF1201S.getFileName());
DF1201S.playSpecFile("/three.mp3");
// Print name of file that's playing
Serial.println(DF1201S.getFileName());

This plays three different songs correctly and their filenames are reported accurately.

HTH everyone 🙂




If you'd like to subscribe to this blog, please click here.

What is an Embedded System and what does Arduino have to do with it?

An Embedded Computer System is a small computer that is “embedded” inside something else. Unlike a laptop computer, or a desktop, or a server, its primary purpose isn’t processing data, but it is meant to control some aspect(s) of the “thing” it’s embedded into. You probably don’t think of your clothes washing machine as a computer, but if it was built in the last 10 years, it almost certainly has an embedded computer controlling it. Likewise, the car or bus that takes you to work has dozens of embedded computers doing tasks as complex as sequencing the operation of the engine, or as basic as raising and lowering the windows.

How big are they?

A typical mainstream CPU (Central Processing Unit: the heart of the computer) such as an AMD Ryzen runs at a clock speed of almost 4GHz, requires more than 65W of power and has over 900 pins for external connections.

By contrast, the majority of embedded systems are based on microcontrollers. A microcontroller is a single package that houses a CPU, perhaps a basic clock source, some RAM memory to hold data and Flash storage for the program that it will execute, and peripherals that can read inputs like switches and pushbuttons, measure temperatures and voltages, and control output devices.

A basic AVR Mega microcontroller that might be found controlling the functions in your washer will run on substantially less than 1W of power, has a nominal clock speed of 8Mhz (but can run much slower to save power) and might require fewer than 20 pins to control such things as water flow, agitator cycling or spinning at the end of cycle. The microcontroller in your toothbrush might only have 6 pins and could run for years on a single AAA cell if necessary.

At the other end of the spectrum, more modern 32-bit microcontrollers have 1M+ of Flash memory and 256kb of RAM. Some microcontrollers can access external memory , further expanding their usefulness.

Why use them?

The benefit that an embedded system has over using logic “chip” to do their operations as was more popular in the past is primarily that a single low-cost chip can replace potentially dozens of external chips, thereby saving space, power and, of course, money. Additionally, a manufacturer may base a number of their products on a single microcontroller type, thereby saving on inventory costs and training. Since microcontrollers are computers at their heart, they can be reprogrammed to perform functions specific to the product they are embedded into.

Where does Arduino come in?

The Arduino is a basic microcontroller platform that was initially intended to offer artists and hobbyists a simple way to get involved in embedded systems. It has expanded from its basic roots to become a huge Open Source ecosystem of programming tools, libraries that make it easy to program unique functionality and add-on hardware that offers motion control, temperature and humidity measurement, multicolor LED displays, sophisticated graphics and so on.

Arduino continues to be a great way to get your feet wet in programming embedded systems.




If you'd like to subscribe to this blog, please click here.

First KiCAD boards

All these years I’ve used Eagle to design PCBs. Finally decide to try KiCAD and I’m loving it. The part editor is so much easier to use and you know that’s one of the major tasks of making a new board.

This particular design is a custom Bluetooth audio interface based on an ESP32.




If you'd like to subscribe to this blog, please click here.