I had a small project that need to deliver firmware for atmega32u4 for my customers. And of course, they aren’t tech-savvy like me. It was impossible to guide them to use arduino to flash directly using source code. More importantly, I don’t want to share my source code. There is only one option for me that build an avrdude wrapper that detect atmega32u4 and run avrdude automatically. This tutorial is about uploading firmware to atmega32u4 programmatically using AVR109 app note in Visual Studio.
INDEX
[INDENT]- Collecting a suitable version of avrdude for just atmega32u4
- Uploading hex to Pro Micro - atmega32u4
- Detect COM port that leonardo bootloader uses
- Force reset Leonardo into bootloader
- Run avrdude using system()
- Setup a file server to store fw and tools
[/INDENT]
1. CHOOSING A VERSION OF AVRDUDE
The newest avrdude is 6.3 (465 KB) that comes with a huge configuration file avrdude.conf (480 KB). The configuration file is meant for various AVR microcontroller families and it’s big.
You can use the newest version and the full configuration file and that’s it.
For me I’d like to use the older avrdude which is smaller and also use a trim configuration file for just atmega32u4 only (7 KB). Why? Because I will put the avrdude and configuration file on my file server, so that it will be downloaded every times updater program is run. All I need is just deliver a small executable file and it takes just a second to download everything unless your customer’s still using dial-up. If I need to build new firmware for my customer, I don’t have to rebuild the updater for this matter.
You can get binary releases of avrdude here or just use the old version with trim configuration: [attach=downloads/m32u4_prog/avrdude_m32u4.rar]avrdude_m32u4.rar[/attach]
2. USING AVRDUDE TO UPLOAD
Almost all atmega32u4 boards out there, clones and originals such as sparkfun Pro Micro or Arduino Micro, can be programmed the same way, using avr109 bootloader protocol. Basically, you run a simple console command like this:
avrdude.exe -Cavrdude.conf -pm32u4 -cavr109 -D -P COM2 -b57600 -Uflash:w:firmware01.hex:i
Let assume the clones and Pro Micro use similar bootloader to arduino leonardo and arduino driver is used for this project.
If your target board has a blank chip, it will stay on bootloader and will wait for programming forever. If not, it will stop on bootloader for about 8s then proceed to main program. In this small time window, your system detects new hardware, installs driver and then you can see what COM port is used in device manager, but you have to be fast. Run that command in 8s time frame to upload code. COM2 is used as example in previous command.
[INDENT][/INDENT]
This is what shows on PC without arduino drivers. The exact same name for bootloader just with different COM port.
3. DETECT LEONARDO BOOTLOADER COM PORT
There are a few methods to obtain the list of COM ports:
- Try open COM port from COM1 to COM16, lets hope there aren’t so many occupied COM ports .
- Get query from WMI
- Read registry to get list of COM ports
First method is likely impossible, although I used it for update time for my Word clock and or Analog clock.
The second method is kind of complex, I don’t want to learn this kind of SQL-language-like just to enumerate COM ports. Well, if you are passionate about programming, you can dig deeper into this WMI. Who knows, you may get a lot of exotic things.
The third method is best one IMO, fastest one too. Each time computer enumerates a device, it will update registry with the new connected device. The first location that looks interesting is
Computer\HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM
//In short, I will use HKLM
Only small problem is, there is no friendly name in these list. This is useful for searching other Serial Devices as well.
But this could be improved by just reading another interesting registry location
HKLM\SYSTEM\CurrentControlSet\Services\usbser\Enum
This location contains the list of active USB serial devices which Arduino Leonardo and alike are. The previous location says 3 COM devices but this location specifically says there is only 1 serial device as indicated by by DWORD value Count. Use count id string (start with 0 instead of 1) to get the related data of these enumerated devices
HKLM\SYSTEM\CurrentControlSet\Enum\USB\VID_2341&PID_8036&MI_00\9&88cb209&0&0000
Your USB VID, PID or code after may be different, but that is okay since you just copy it into the next registry query.
Then we just simply add each USB serial device found to a custom struct, USBSER.
struct USBSER{
INT PORT_NO;
TCHAR Name[256];
};
You can use this header for doing all of that
[attach=downloads/m32u4_prog/reg_q.h]reg_q.h[/attach] - get list of USB serial device using registry query
4. FORCE RESET LEONARDO INTO BOOTLOADER
Either you hit reset button on the little board or unplug and plug it in again to reset it into bootloader, that is how you do it when upload sketch from Arduino IDE.
Another method is open COM port with baud rate 1200 then close. This will also trigger reset of USB connection.
HANDLE serialHandle;
TCHAR buffer32[32];
formatString(buffer32,L"\\\\.\\COM%d\\", PORTNO);
serialHandle = CreateFile((LPCWSTR)buffer32, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
//_COM_busy = TRUE;
if (serialHandle)
{
DCB serialParams = { 0 };
serialParams.DCBlength = sizeof(serialParams);
GetCommState(serialHandle, &serialParams);
serialParams.BaudRate = CBR_1200;
serialParams.fDtrControl = DTR_CONTROL_DISABLE;
serialParams.fRtsControl = RTS_CONTROL_ENABLE;
SetCommState(serialHandle, &serialParams);
}
CloseHandle (serialHandle);
//_COM_busy = FALSE;
Only a small problem is different COM port is assigned to Leonardo bootloader. No worries, just call GetListUSBser() in reg_q.h to get a list of USB serial device and compare with the list before force reset. Or you can check for the friendly name Arduino Leonardo bootloader specifically, assuming Arduino driver installed.
int ntime = 40;
int PROGRAM_PORT=0;
while(ntime > 0 && PROGRAM_PORT == 0)
{
Sleep (250);
USBSER us[10];
GetListUSBser(us); // check port again to get any bootloader device.
TCHAR bl= 'b';
for (int k = 0; k < 10; k++)
{
if (us[k].PORT_NO == 0) break; // there is no such COM port COM0
if (us[k].Name[17] == bl) // if matching character 'b' in Arduino Leonardo bootloader
{
PROGRAM_PORT = us[k].PORT_NO;
break;
}
}
ntime--;
//WriteLog(L"Scanning...");
}
If maybe your customer doesn’t have Arduino driver installed, the name would be USB Serial Device instead. So just compare the list USBSER before and after force reset for a new COM port that hasn’t showed up before. The code would be little complex
int ntime = 40;
int PROGRAM_PORT=0;
USBSER USBser1[10];
GetListUSBser(USBser1);
forceReset();
while(ntime > 0 && PROGRAM_PORT == 0)
{
Sleep (250);
USBSER USBser2[10];
GetListUSBser(USBser2);
for (int k = 0; k < 10; k++)
{
if (USBser2[k].PORT_NO == 0) break; // no such port COM0
BOOL portMatch = false;
for (int j = 0; j<10; j++)
if (USBser2[k].PORT_NO == USBser1[j].PORT_NO) {
portMatch = true;
break;
}
if (portMatch) continue; // got a match, next loop
else { // first mismatching after force reset
PROGRAM_PORT = USBser2[k].PORT_NO;
break;
}
}
}
5. RUN AVRDUDE USING SYSTEM()
This step is very easy. Prepare the command, then call it using system() with PROGRAM_PORT from previous step
char cmd[1024]=0;
sprintf(cmd, "avrdude -C def.con -pm32u4 -c avr109 -D -P COM%d -b57600 -Uflash:w:firmware01.hex:i", PROGRAM_PORT);
system(cmd);
There is a way to get message from avrdude but not needed in this project.
And finally, build a nice dialog for your uploader program, like what I did for ac2usb
6. SETUP SERVER TO STORE FW AND TOOLS
You don’t have to do this, just include avrdude.exe, libusb0.dll and avrdude.conf with your firmware updater program. But if you don’t want the customer use the fw to flash to more than 1 leonardo device as he supposes to, well, this could be a problem. With a file server, you can control how many times customer can flash (download a firmware). Just simple php file to return the desire download that look similar to this:
// file: download.php
//calling url for download: abcxyz.com/download.php?file=avrdude
<?php
$cfile = $_GET["file"];
$fw_root = $_SERVER["DOCUMENT_ROOT"] . "/fw/";
$fw_avrdude = $fw_root ."avrdude.exe";
//get avrdude
if ($cfile == "avrdude")
{
header($_SERVER["SERVER_PROTOCOL"] . " 200 OK");
header("Cache-Control: public"); // needed for internet explorer
header("Content-Type: application/zip");
header("Content-Transfer-Encoding: Binary");
header("Content-Length:".filesize($fw_avrdude));
header("Content-Disposition: attachment; filename=avrdude.exe");
readfile($fw_avrdude);
//rename or delete the firmware after user download
//or use a separate URL query to delete firmware after successful flash, such as abcxyz.com/download.php?success=true
die();
}
?>
You can do the same for avrdude.conf and libusb0.dll and also your firmware.
Now come to the tricky part. You want your program download these files automatically like how those online installers. With VC++, you have the option to do that quite easy using function URLDownloadToFile():
#pragma comment (lib, "urlmon.lib") // Link to the library for compiling
#include <Urlmon.h>
#define TEXT_URL L"abcxyz.com/download.php"
//#define MAX_PATH 260
int main()
{
WCHAR base_url[] = TEXT_URL;
WCHAR file_url[256] = {0};
swprintf(file_url, L"%s?file=avrdude", base_url); // abcxyz.com/download.php?file=avrdude
WCHAR path[MAX_PATH];
GetTempPathW(MAX_PATH, avrfile); // C:\Users\ABCXYZ\AppData\Local\Temp
WCHAR avrfile[MAX_PATH];
swprintf(avrfile, L"%savrdude.exe", avrfile); // C:\Users\ABCXYZ\AppData\Local\Temp\avrdude.exe
HRESULT hRes = URLDownloadToFileW(NULL, file_url, avrfile, 0, NULL);
}
7. CONCLUSION
It certainly needs more thoughts into this to be able to build your own atmega32u4 code uploader program. It’s a pity that I cannot share my code for ac2usb fw uploader. But you already have most of the bricks available. You can build one for yourself.
The codes in this entry doesn’t have a licence, you can do whatever you want with it.