CNN for MCUs, some links

https://github.com/jonnor/ESC-CNN-microcontroller

https://bdtechtalks.com/2022/01/17/mcunetv2-tinyml-deep-learning-microcontrollers/ 

https://eloquentarduino.github.io/2020/11/tinyml-on-arduino-and-stm32-cnn-convolutional-neural-network-example/

MCU with CNN: https://www.maximintegrated.com/en/products/microcontrollers/MAX78000.html

Converting Chinese text from GB18030 to UTF-8

I was trying to convert a text from chinese that was “scrambled” like this:

4£©ÍêÉÆAACœâÂëÒì³£ÎÊÌ⣻
5£©ÍêÉÆBT VOLͬ²œ¹ŠÄÜ£»
6£©ÍêÉÆÀ¶ÑÀžèÇúID3ÐÅÏ¢Òì³£ÎÊÌâ;
7£©ÐÞžŽÀ¶ÑÀ¿ª»ú»ØÁ¬¡¢ÊÖ»úÖ÷¶¯Á¬œÓ£¬·ŽžŽµÄ²Ù×÷£¬»á³öÏÖÒì³£žŽÎ»ÎÊÌ⣻
8£©À¶ÑÀ²¥·ÅìÒ»úÎȶšÐÔÍêÉÆ£»
9£©À¶ÑÀÍš»°Ïà¹ØŒæÈÝÐÔ£¬œ¡×³ÐÔÍêÉÆ£»
10£©ÔöŒÓÊÖ»úÀŽµç²¥·Å±ŸµØÌáÊŸÒôµÄ¹ŠÄÜ£¬ŒûCFG_BT_RING_LOCAL²¿·Ö£»
3.HDMI/ͬÖá/¹âÏËÏà¹ØžüУº
1£©JVC£¬º£ÐÅ,СÃ×Æ·ÅƵçÊÓŒæÈÝÐÔÍêÉÆ£»

Then I used the same strategy from here.

$ uchardet Chinese_Scrambled_Text.txt
GB18030
$ iconv -f GB18030 -t UTF-8//TRANSLIT Chinese_Scrambled_Text -o Chinese_Readable_UTF8.txt

Getting OpenOCD to work with Ai-Thinker VC-01

Continuing my adventure with VC-01 module (powered by Unisound US516P6) I found the OpenOCD used with JTAG programmer (VC_Burner B228) and fixed the configuration to get it working (WIP).

First we need to clone this specific openocd repository:

$ git clone https://github.com/andestech/nds-openocd

Then compile it (I enabled all the programmers supported by OpenOCD, but you just need aice)

$ ./bootstrap
$ ./configure --enable-internal-jimtcl --enable-maintainer-mode --disable-werror --disable-shared --enable-stlink --enable-aice --enable-jlink --enable-rlink --enable-vslink --enable-ti-icdi --enable-remote-bitbang --enable-usb-blaster --enable-presto --enable-osbdm
$ sudo make install

You need this OpenOCD config file:

$ cat openocd_nds32.cfg 
log_output iceman_debug0.log
debug_level 3
gdb_port 9902
telnet_port 9901
tcl_port 6666
source [find interface/nds32-aice.cfg]
source [find board/nds32_xc5.cfg]

gdb_target_description enable
gdb_report_data_abort enable
nds log_file_size 10485760
nds global_stop off
nds reset_halt_as_init on
nds boot_time 5000
nds reset_time 1000

Note: you need to edit /usr/local/share/openocd/scripts/interface/nds32-aice.cfg and use:

adapter_khz 12000

instead of 24000!


$ sudo openocd -f openocd_nds32.cfg -d3
Open On-Chip Debugger 0.10.0+dev-g47292a9c3 (2022-11-14-13:07)
Licensed under GNU GPL v2
For bug reports, read
	http://openocd.org/doc/doxygen/bugs.html
User : 91 1 options.c:60 configuration_output_handler(): debug_level: 3
User : 92 1 options.c:60 configuration_output_handler(): 
Debug: 93 1 options.c:184 add_default_dirs(): bindir=/usr/local/bin
Debug: 94 1 options.c:185 add_default_dirs(): pkgdatadir=/usr/local/share/openocd
Debug: 95 1 options.c:186 add_default_dirs(): exepath=/usr/local/bin
Debug: 96 1 options.c:187 add_default_dirs(): bin2data=../share/openocd
Debug: 97 1 configuration.c:42 add_script_search_dir(): adding /root/.openocd
Debug: 98 1 configuration.c:42 add_script_search_dir(): adding /usr/local/bin/../share/openocd/site
Debug: 99 1 configuration.c:42 add_script_search_dir(): adding /usr/local/bin/../share/openocd/scripts
Debug: 100 1 configuration.c:97 find_file(): found openocd_nds32.cfg
Debug: 101 1 command.c:147 script_debug(): command - log_output log_output iceman_debug0.log
Andes AICE-MINI v2.0.0
There is 1 core in target
JTAG frequency 12 MHz
<-- HW reset-and-hold failed. -->
<-- SW reset-and-hold failed. -->
<-- aice_issue_srst ERROR! -->
The core #0 listens on 9902.
ICEman is ready to use.


Testing HMAC MD5 on ESP32

I modified the hello_world example to do same HMAC test I did in the PC:

#define MD5_MAC_LEN 16

extern int md5_vector(size_t num_elem, const uint8_t *addr[], const size_t *len, uint8_t *mac);

int hmac_md5_vector(const uint8_t *key, size_t key_len, size_t num_elem,
                    const uint8_t *addr[], const size_t *len, uint8_t *mac)
{
  uint8_t k_pad[64]; /* padding - key XORd with ipad/opad */
  uint8_t tk[16];
  const uint8_t *_addr[6];
  size_t i, _len[6];
  int res;

  if (num_elem > 5)
    {
      /*
       * Fixed limit on the number of fragments to avoid having to
       * allocate memory (which could fail).
       */

      return -1;
    }

  /* if key is longer than 64 bytes reset it to key = MD5(key) */

  if (key_len > 64)
    {
      if (md5_vector(1, &key, &key_len, tk))
        {
      	  return -1;
        }

      key = tk;
      key_len = 16;
    }

  /* the HMAC_MD5 transform looks like:
   *
   * MD5(K XOR opad, MD5(K XOR ipad, text))
   *
   * where K is an n byte key
   * ipad is the byte 0x36 repeated 64 times
   * opad is the byte 0x5c repeated 64 times
   * and text is the data being protected */

  /* start out by storing key in ipad */

  memset(k_pad, 0, sizeof(k_pad));
  memcpy(k_pad, key, key_len);

  /* XOR key with ipad values */

  for (i = 0; i < 64; i++)
    {
      k_pad[i] ^= 0x36;
    }
     
  /* perform inner MD5 */

  _addr[0] = k_pad;
  _len[0] = 64;

  for (i = 0; i < num_elem; i++)
    {
      _addr[i + 1] = addr[i];
      _len[i + 1] = len[i]; 
    }

  if (md5_vector(1 + num_elem, _addr, _len, mac))
    {
	  return -1;
    }

  memset(k_pad, 0, sizeof(k_pad));
  memcpy(k_pad, key, key_len);

  /* XOR key with opad values */

  for (i = 0; i < 64; i++)
    {
      k_pad[i] ^= 0x5c;
    }

  /* perform outer MD5 */

  _addr[0] = k_pad;
  _len[0] = 64;
  _addr[1] = mac;
  _len[1] = MD5_MAC_LEN;
  res = md5_vector(2, _addr, _len, mac);
  memset(k_pad, 0, sizeof(k_pad));
  memset(tk, 0, sizeof(tk));

  return res;
}

int hmac_md5(const uint8_t *key, size_t key_len, const uint8_t *data, size_t data_len,
             uint8_t *mac)
{
  return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
}

int main(int argc, FAR char *argv[])
{
  int i;
  uint8_t key[8] = {'S', '3', 'c', 'r', 'e', 't', 's', '!'};
  const uint8_t msg[16] = {'M', 'y', ' ', 'M', 'e', 's', 's', 'a', 'g', 'e', ' ', 'p', 'l', 'a', 'i', 'n'};
  uint8_t hmac[128];

  memset(hmac, 0x00, 128);

  hmac_md5(key, 8, msg, 16, hmac);

  for (i = 0; i < 16; i++)
    {
      printf("hmac[%d] = %02X\n", i, hmac[i]);
    }

  return 0;
}

The result was the same:

nsh> hello
hmac[0] = 3D
hmac[1] = 32
hmac[2] = 76
hmac[3] = FD
hmac[4] = E3
hmac[5] = FB
hmac[6] = 42
hmac[7] = D8
hmac[8] = F0
hmac[9] = 05
hmac[10] = 53
hmac[11] = CC
hmac[12] = 12
hmac[13] = E7
hmac[14] = C9
hmac[15] = C3
nsh>

Compiling a simples HMAC MD5 test

I was trying do some tests with HMAC:

#include <stdint.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <openssl/hmac.h>
#include <openssl/ossl_typ.h>

static int openssl_hmac_vector(const EVP_MD *type, const uint8_t *key,
			       size_t key_len, size_t num_elem,
			       const uint8_t *addr[], const size_t *len, uint8_t *mac,
			       unsigned int mdlen)
{
	HMAC_CTX *ctx;
	size_t i;
	int res;

	ctx = HMAC_CTX_new();
#if OPENSSL_VERSION_NUMBER < 0x00909000
	HMAC_Init_ex(ctx, key, key_len, type, NULL);
#else /* openssl < 0.9.9 */
	if (HMAC_Init_ex(ctx, key, key_len, type, NULL) != 1)
		return -1;
#endif /* openssl < 0.9.9 */

	for (i = 0; i < num_elem; i++)
		HMAC_Update(ctx, addr[i], len[i]);

#if OPENSSL_VERSION_NUMBER < 0x00909000
	HMAC_Final(ctx, mac, &mdlen);
	res = 1;
#else /* openssl < 0.9.9 */
	res = HMAC_Final(ctx, mac, &mdlen);
#endif /* openssl < 0.9.9 */
	HMAC_CTX_free(ctx);

	return res == 1 ? 0 : -1;
}

int hmac_md5_vector(const uint8_t *key, size_t key_len, size_t num_elem,
		    const uint8_t *addr[], const size_t *len, uint8_t *mac)
{
	return openssl_hmac_vector(EVP_md5(), key ,key_len, num_elem, addr, len,
				   mac, 16);
}

int hmac_md5(const uint8_t *key, size_t key_len, const uint8_t *data, size_t data_len,
	     uint8_t *mac)
{
	return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
}

int main(void)
{
  int i;
  uint8_t key[8] = {'S', '3', 'c', 'r', 'e', 't', 's', '!'};
  const uint8_t msg[16] = {'M', 'y', ' ', 'M', 'e', 's', 's', 'a', 'g', 'e', ' ', 'p', 'l', 'a', 'i', 'n'};
  uint8_t hmac[128];

  memset(hmac, 0x00, 128);

  hmac_md5(key, 8, msg, 16, hmac);

  for (i = 0; i < 16; i++)
    {
      printf("hmac[%d] = %02X\n", i, hmac[i]);
    }

  return 0;
}

But it was failing to compile:

$ gcc testhmac.c -o testhmac
/usr/bin/ld: /tmp/cczEFXg9.o: in function `openssl_hmac_vector':
testhmac.c:(.text+0x25): undefined reference to `HMAC_CTX_new'
/usr/bin/ld: testhmac.c:(.text+0x4e): undefined reference to `HMAC_Init_ex'
/usr/bin/ld: testhmac.c:(.text+0xa2): undefined reference to `HMAC_Update'
/usr/bin/ld: testhmac.c:(.text+0xc5): undefined reference to `HMAC_Final'
/usr/bin/ld: testhmac.c:(.text+0xd4): undefined reference to `HMAC_CTX_free'
/usr/bin/ld: /tmp/cczEFXg9.o: in function `hmac_md5_vector':
testhmac.c:(.text+0x111): undefined reference to `EVP_md5'
collect2: error: ld returned 1 exit status

Passing -lssl didn’t solve the issue:

$ gcc testhmac.c -o testhmac -lssl
/usr/bin/ld: /tmp/ccRUYEU0.o: in function `openssl_hmac_vector':
testhmac.c:(.text+0x25): undefined reference to `HMAC_CTX_new'
/usr/bin/ld: testhmac.c:(.text+0x4e): undefined reference to `HMAC_Init_ex'
/usr/bin/ld: testhmac.c:(.text+0xa2): undefined reference to `HMAC_Update'
/usr/bin/ld: testhmac.c:(.text+0xc5): undefined reference to `HMAC_Final'
/usr/bin/ld: testhmac.c:(.text+0xd4): undefined reference to `HMAC_CTX_free'
/usr/bin/ld: /tmp/ccRUYEU0.o: in function `hmac_md5_vector':
testhmac.c:(.text+0x111): undefined reference to `EVP_md5'
collect2: error: ld returned 1 exit status

You need to use -lcrypto (some people suggest -lssl -lcrypto)

$ gcc testhmac.c -o testhmac -lcrypto

Now it compiled fine and I can run it:

$ ./testhmac 
hmac[0] = 3D
hmac[1] = 32
hmac[2] = 76
hmac[3] = FD
hmac[4] = E3
hmac[5] = FB
hmac[6] = 42
hmac[7] = D8
hmac[8] = F0
hmac[9] = 05
hmac[10] = 53
hmac[11] = CC
hmac[12] = 12
hmac[13] = E7
hmac[14] = C9
hmac[15] = C3

Trying to fix a MQTT communication issue with NuttX and TagoIO

As you saw in my previous post I was able to get the same mqttc example from NuttX running on Linux.

Unfortunately the NuttX application is not working, then I decided to run the WireShark to compare what the Linux application was doing different from the NuttX application.

It should be easier to sniffer the network if it was Ethernet instead WiFi, so I used the ESP32-Ethernet-Kit board to test with NuttX and a RaspberryPI 3 to test with Linux (I tried to use other computer with Linux, but there are so many packages from Linux services that it was also impossible to compare).

Initially I connected both boards to my WiFi router RJ45 ports, but the router seems to isolate each port, so it is working like an Ethernet Switch instead of working like an Ethernet HUB.

Then I decided to use my laptop to share Internet with those board (one by time). I remember that I did it almost 15 years ago and documented it here (sorry in Portuguese).

That idea worked fine, that is a good solution for future test!

Initially I noticed some strange things: first the NuttX was with “MSS=536” (the minimum MSS size supported by IPv4) where the Linux was using “MSS=1460”

NuttX:
3	0.050060828	192.168.1.100	75.2.83.130	TCP	60	4097 → 1883 [SYN] Seq=0 Win=5488 Len=0 MSS=536

Linux:
7	1.359660410	192.168.1.100	99.83.172.119	TCP	74	40264 → 1883 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2409185516 TSecr=0 WS=128

Maybe these differences could explain why NuttX was not working, but unfortunately increasing the MSS to 1460 didn’t help, I still receiving a Ack=0 from the server:

6 34.353497310 192.168.1.100 75.2.83.130 TCP 60 4097 → 1883 [SYN] Seq=0 Win=5488 Len=0 MSS=1446
7 34.496251127 75.2.83.130 192.168.1.100 TCP 58 1883 → 4097 [SYN, ACK] Seq=0 Ack=0 Win=65535 Len=0 MSS=0

So, let to test other ideas, I also noticed that the Linux application was using a big Window (Win=64240), the “Selective Acknowledgements” (SACK_PERM=1), time stamp (TSval=2409185516 TSecr=0) and Window Size Scaling (WS=128)

Increasing the Window to 65KB doesn’t sound like a good idea, let’s to modify the other Linux TCP stack parameters to become similar to NuttX:

$ sudo sysctl -w net.ipv4.tcp_sack=0
$ sudo sysctl -w net.ipv4.tcp_timestamps=0
$ sudo sysctl -w net.ipv4.tcp_window_scaling=0

These modifications also didn’t solve the issue, then I also tried to reduce the Win to 4KB:

# echo "4096 4096 4096" > /proc/sys/net/ipv4/tcp_rmem
# echo "4096 4096 4096" > /proc/sys/net/ipv4/tcp_wmem

Another nice idea is to reduce the MTU to 576 and consequently the MSS becomes 536:

$ sudo ifconfig eth0 mtu 576 up

It also didn’t make the issue show up on Linux, so there is some other detail, see complete Linux log:

1	0.000000000	192.168.1.100	8.8.8.8	DNS	72	Standard query 0x6055 A mqtt.tago.io
2	0.041538839	8.8.8.8	192.168.1.100	DNS	104	Standard query response 0x6055 A mqtt.tago.io A 99.83.172.119 A 75.2.83.130
3	0.043238286	192.168.1.100	99.83.172.119	TCP	60	59880 → 1883 [SYN] Seq=0 Win=1460 Len=0 MSS=1460
4	0.190940161	99.83.172.119	192.168.1.100	TCP	58	1883 → 59880 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0 MSS=1416
5	0.191451199	192.168.1.100	99.83.172.119	TCP	60	59880 → 1883 [ACK] Seq=1 Ack=1 Win=1460 Len=0
6	0.192109946	192.168.1.100	99.83.172.119	MQTT	117	Connect Command
7	0.371321172	99.83.172.119	192.168.1.100	TCP	54	1883 → 59880 [ACK] Seq=1 Ack=64 Win=65535 Len=0
8	0.371805531	192.168.1.100	99.83.172.119	MQTT	111	Publish Message [tago/data/post]
9	0.467783276	99.83.172.119	192.168.1.100	MQTT	58	Connect Ack
10	0.468174062	192.168.1.100	99.83.172.119	TCP	60	59880 → 1883 [ACK] Seq=121 Ack=5 Win=1456 Len=0
11	0.515294572	99.83.172.119	192.168.1.100	TCP	54	1883 → 59880 [ACK] Seq=5 Ack=121 Win=65535 Len=0
12	5.098640506	20:7b:d2:0d:a8:d0	Raspberr_65:2a:1a	ARP	42	Who has 192.168.1.100? Tell 192.168.1.1
13	5.099071927	Raspberr_65:2a:1a	20:7b:d2:0d:a8:d0	ARP	60	192.168.1.100 is at b8:27:eb:65:2a:1a
14	5.193192947	192.168.1.100	99.83.172.119	MQTT	60	Disconnect Req
15	5.193699389	192.168.1.100	99.83.172.119	TCP	60	59880 → 1883 [FIN, ACK] Seq=123 Ack=5 Win=1456 Len=0
16	5.338709513	99.83.172.119	192.168.1.100	TCP	54	1883 → 59880 [ACK] Seq=5 Ack=123 Win=65535 Len=0
17	5.338714704	99.83.172.119	192.168.1.100	TCP	54	1883 → 59880 [FIN, ACK] Seq=5 Ack=124 Win=65535 Len=0
18	5.339267333	192.168.1.100	99.83.172.119	TCP	60	59880 → 1883 [ACK] Seq=124 Ack=6 Win=1455 Len=0