Thursday, 9 July 2020

Using yet another CH340 USB dongle as ESP-01S Programmer


You Can't Always Get What You Want

In my previous post I modified a CH340 USB to ESP8266 adapter. It worked well, but after programming an ESP-01S I kept having to re-plug it into the USB port in order to test it. Wouldn't it be nice if I could buy one with a built-in reset switch?


Diymore CH340G USB to ESP8266 ESP-01S Adapter



The DIYMore CH340G USB to ESP8266 Adapter looked like just the ticket. There's that built-in switch. and I know the CH340 worked well with my Arduino installation. And at RM5.24 it was even cheaper than the old model.

I guess if things looked too good to be true on the Internet, sometimes it is. It did not work. While it implemented the CH_PD to GPIO 0 switch, it did not pull GPIO 2 and CH_PD high, so the ESP-01S does not start up, and will neither run nor accept a program. 

ESP-01S Pinout


The cure is the same as that for the WiFi ESP8266 ESP-01S Relay module: short CH_PD to VCC and connect a 10K resistor between GPIO 2 and VCC. The modified Diymore CH340 USB to ESP8266 Adapter now looks like this:

Short CH_PD to VCC and connect a 10K resistor from GPIO 0 to VCC

Now it works. To program, you flip the switch to 'PROG' and plug the dongle in. You then flip it back to 'UART' and flash your Arduino IDE sketch. To run, you plug it in with the switch at 'UART'.

Except I still have to unplug and plug it in to test my new programs. I guess you can't always get what you want.  But if you try, sometimes you'll find you get what you need.

Happy Trails. 


Wednesday, 8 July 2020

Flashed Before My Eyes: Flashing firmware of WiFi 2-Channel Relay Board Part 2 of 2


Nu-Link programmer (bottom) with LC Technology's WiFi Relay Board

In Part 1, we encountered a 'Device ID' problem reading the N76E003AT20 CPU in the LC Technology WiFi Relay Board. The CPU appeared to be responding to the programmer's queries, so I checked the nuvoprog source code. In the file protocol/commands.go was the code to check for Device ID:

type DeviceID uint32
const (
        // N76E003, Observed in trace
        //
        // Matches IAP registers:
        //   0x00CCDDDD where
        //              CC   = Company ID
        //              DDDD = Device ID
        DeviceN76E003 = 0xDA3650
)

And since my relay board's N76E003 was returning 0xff3650, I changed it to 

type DeviceID uint32
const (
        // N76E003, Observed in trace
        //
        // Matches IAP registers:
        //   0x00CCDDDD where
        //              CC   = Company ID
        //              DDDD = Device ID
        // DeviceN76E003 = 0xDA3650
        DeviceN76E003 = 0xFF3650
)

Now remember to put your nuvoton directory into /root/go/ for the build only works for that directory listed in the environmant variable GOPATH:

# go env GOPATH
/root/go

# go build

And indeed there is a new nuvoprog (check the timestamp):
# ls -l
total 5224
drwxr-xr-x 2 root root    4096 Jul  6 17:15 cmd
drwxr-xr-x 2 root root    4096 Jul  6 17:15 ihex
-rw-r--r-- 1 root root   11358 Jul  6 17:15 LICENSE
-rw-r--r-- 1 root root     688 Jul  6 17:15 main.go
drwxr-xr-x 2 root root    4096 Jul  6 17:15 misc
-rwxr-xr-x 1 root root 5306088 Jul  7 15:46 nuvoprog
drwxr-xr-x 2 root root    4096 Jul  7 15:44 protocol
-rw-r--r-- 1 root root    2812 Jul  6 17:15 README.md
drwxr-xr-x 4 root root    4096 Jul  6 17:15 target

And now the programmer reads back properly:
# ./nuvoprog read -t n76e003  2relay_readback_good.ihx -v
.
.
.
2020/07/07 15:46:45 Performing reset {None (NuLink) Disconnect Ext Mode}
2020/07/07 15:46:45 >  0c10e2000000050000000400000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000
2020/07/07 15:46:45 <  0c04e2000000fffffffffffffffffffffffffffffffffffffffffffff
fffffffffff000046662915699c03b4c74319896b6b8896664ef446d10e91c0d10a904c
2020/07/07 15:46:45 OK

Now despite a working relay board the program file read back is actually blank:

# head 2relay_readback_good.ihx
:020000040003F7
:08000000FDFFFFFFFFFFFFFF02
:020000040000FA
:20000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00
:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40

A check of the relay CPU configuration revealed the cause: it has been locked:

# ./nuvoprog config decode -t n76e003 -i 2relay_readback_good.ihx
{
    "boot_select": "aprom",
    "pwm_enabled_during_ocd": false,
    "ocd_enabled": false,
    "reset_pin_disabled": false,
    "locked": true,
    "ldrom_size": "0kb",
    "bod_disabled": false,
    "bod_voltage": "2v2",
    "iap_enabled_in_brownout": false,
    "bod_reset_disabled": false,
    "wdt": "disabled"
}

For comparison, the config file of the Core Controller was:
{
    "boot_select": "aprom",
    "pwm_enabled_during_ocd": false,
    "ocd_enabled": true,
    "reset_pin_disabled": false,
    "locked": false,
    "ldrom_size": "0kb",
    "bod_disabled": false,
    "bod_voltage": "2v2",
    "iap_enabled_in_brownout": false,
    "bod_reset_disabled": false,
    "wdt": "disabled"
}

The relay board CPU programming command returned no error once:
# ./nuvoprog program -t n76e003 -a blink.ihx -c @config.json -v
2020/07/07 15:57:01 Performing reset {Auto Disconnect 0x00000001}
2020/07/07 15:57:01 >  0c10e2000000000000000400000001000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000
2020/07/07 15:57:01 <  0c04e20000005036ff00f8ffffff57012d0100002000fffffffffffff
fffffffffff000046662915699c03b4c74319896b6b8896664ef446d10e91c0d10a904c
2020/07/07 15:57:01 OK
2020/07/07 15:57:01 Performing reset {None (NuLink) Disconnect Ext Mode}
2020/07/07 15:57:01 >  0d10e2000000050000000400000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000
2020/07/07 15:57:01 <  0d04e20000005036ff00f8ffffff57012d0100002000fffffffffffff
fffffffffff000046662915699c03b4c74319896b6b8896664ef446d10e91c0d10a904c
2020/07/07 15:57:01 OK
 
But a read-back confirmed that the device ID has changed back to da3650:
# ./nuvoprog read -t n76e003  2relay_readback_bad.ihx
Error: Unsupported device
Usage:
  nuvoprog read [outfile.ihx] [flags]

Flags:
  -h, --help   help for read

Global Flags:
  -t, --target string   target device
  -v, --verbose         make verbose (enable debug logging)

Unsupported device

And the file is blank, so the 'program' action merely erased the existing relay program:

# head  2relay_readback_bad.ihx
:020000040003F7
:08000000FFFFFFFFFFFFFFFF00
:020000040000FA
:20000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00
:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40

So now having unlocked the relay CPU for programming I now need to revert back to erincandescent's original code:

type DeviceID uint32
const (
        // N76E003, Observed in trace
        //
        // Matches IAP registers:
        //   0x00CCDDDD where
        //              CC   = Company ID
        //              DDDD = Device ID
        DeviceN76E003 = 0xDA3650
)
Whereupon the CPU behaves normally:
# /root/go/bin/nuvoprog program -t n76e003 -a blink.ihx -c @config.json
# /root/go/bin/nuvoprog read -t n76e003  2relay_readback_bad.ihx

            And the new blink.c program took:
# head  2relay_readback_bad.ihx
:020000040003F7
:08000000EFFFFFFFFFFFFFFF10
:020000040000FA
:200000000200060200997581071200E8E58260030200037900E94400601B7A009000EC78E8
:200020000175A000E493F2A308B8000205A0D9F4DAF275A0FFE478FFF6D8FD7800E84400C0
:20004000600A790175A000E4F309D8FC7800E84400600C7900900001E4F0A3D8FCD9FA02B9
:200060000003AC82AD83AEF0FF538EF774FC55894401F589D28CEC4D4E4F601A758ACB75AC
:200080008CFA108D0280FB1CBCFF091DBDFF051EBEFF011F80E0C28C2275B10075B2007575
:2000A000B30075B40075AC0075AD007E007F00C2959002BCE4F5F0C007C006120062D2954E
:2000C0009002BCE4F5F0120062D006D0070EBE00010FC3EE940AEF6480948040D29003E849

Now having wiped the firmware in a perfectly good WiFi Relay board, I need working N76E003 firmware. I need not have worried, for this being the Internet, cometh the hour, cometh the man, necromant. There is even a pre-compiled executable, firmware.hex!
 
I tried programming firmware.hex, but it did not work. And looking at the source code, src/main.c was the reason; the CPU pin assignments are different for the dual relay board. The code read:

#define RELAY4 P01
#define RELAY3 P04
#define RELAY2 P03
#define RELAY1 P05 #define RED P11
#define GREEN P00
#define BLUE P10 #define PWM_GREEN PWM3L
#define PWM_BLUE PWM2L
#define PWM_RED PWM1L #define BUTTON1 P15
#define BUTTON2 P12
And inside the main() loop are more hard code:
int main()
{
uart_init(19200);
printf("\n\n\nHello world. I'm a hacky opensource firmware for LC-tech modules\n");
printf("Go check out my blog https://ncrmnt.org for more awesome stuff\n");
/* Relays */
P01_PushPull_Mode;
P03_PushPull_Mode;
P04_PushPull_Mode;
P05_PushPull_Mode; /* LEDS */
P10_PushPull_Mode;
P11_PushPull_Mode;
P00_PushPull_Mode; /* Buttons */
P12_Input_Mode;
P15_Input_Mode;

By manually tracing out all 20 CPU pins using a multimeter, I corrected the code as:

#define RELAY4 P15
#define RELAY3 P12
#define RELAY2 P15
#define RELAY1 P12
#define RED P01
#define GREEN P04
#define BLUE P03
#define PWM_GREEN PWM3L
#define PWM_BLUE PWM2L
#define PWM_RED PWM1L
#define BUTTON1 P00
#define BUTTON2 P10
int main()
{
        uart_init(19200);
        printf("\n\n\nHello world. I'm a hacky opensource firmware for LC-tech modules\n");
        printf("Go check out my blog https://ncrmnt.org for more awesome stuff\n");
        /* Relays */
        P15_PushPull_Mode; /* Relay 4 */
        P15_PushPull_Mode; /* Relay 2 */
        P12_PushPull_Mode; /* Relay 3 */
        P12_PushPull_Mode; /* Relay 1 */
        /* LEDS */
        P03_PushPull_Mode; /* Blue */
        P01_PushPull_Mode; /* Red */
        P04_PushPull_Mode; /* Green */
        /* Buttons */
        P10_Input_Mode;
        P00_Input_Mode;
        /* buttonz */
        PWMPH = 0;
        PWMPL = 255;
        PWM_CLOCK_FSYS;
        PWM_CLOCK_DIV_128;
        PWM_EDGE_TYPE;
        PWM1_P11_OUTPUT_ENABLE;
        PWM2_P10_OUTPUT_ENABLE;
        PWM3_P00_OUTPUT_ENABLE;
        RED = 0;
        GREEN = 0;
        BLUE = 1;
        RELAY1 = 0;
        RELAY2 = 0;
        RELAY3 = 0;
        RELAY4 = 0;
To keep the changes to a minimum, I modified RELAY3 to be the same as RELAY1, and RELAY4 the same as RELAY2. Also the code defaulted to relays turned on at power-up, and I changed it to off.

Now not having the same IDE as nekromant, and not having the Makefile, I issued the build commands separately:

# sdcc -mmcs51  -c Delay.c -D FOSC_160000 -I../include
 # sdcc -mmcs51  -o 2relays.ihx main.c Delay.rel Common.rel -D FOSC_160000 -I../include
# sdcc -mmcs51  -o 2relays.ihx main.c Delay.rel Common.rel -D FOSC_160000 -I../include

And lastly the programming command:
# /root/go/bin/nuvoprog program -t n76e003 -a ./necromant/lctech-relay-altfw-master/src/2relays.ihx -c @config.json

And the LC Tech WiFi dual relay board worked, just like that. You can pick up the 2-relay version of nekromant's code from my github repository. Life is good.

Happy Trails. 

Tuesday, 7 July 2020

Flashed Before My Eyes: Nuvoton N76E003 CPU Part 1 of 2


Nuvoton N76E003AT20 Core Controller Board

Nowadays the CPUs I use most often are Raspberry Pi, ESP8266 and Arduino. And a couple of ancient Intel-based laptops and of course my ARM-based smartphone. I have desktop PCs which I have not turned on in years.

Software is key:  just because it is more convenient (and just as cheap) to solve a problem using Raspbian or Arduino IDE, I still have stocks of 6502, 8752AH, PIC16F84 and PIC18F14K50 which I have not used in decades. So why bother setting up yet another CPU development system?

At RM6.2 (USD1.50) the Nuvoton N76E003AT20 Core Controller Board compares unfavorably with the Arduino Nano 'compatible' or even the ESP-01S. Just because the last two is supported by the Arduino IDE and it vast library. The N76E003 is usually programmed in C, usually via a proprietary IDE like Keil or IAR.

But it has one thing going for it: it is used in my favorite ESP-01S Relay Board, LC Technology's 5V WiFi Relay.

LC Technology WiFi dual Relay Board

Often pirated and sold as "2-Channel WiFi Relay" at RM19(USD4.50) including the ESP-01S CPU, it has been a mainstay of many IoT projects. Unlike some even cheaper WiFi relay boards, this one uses a serial TTL interface and has its own CPU, the Nuvoton N76E003.

There are two push-buttons for input and 3 LEDs, which can be PWM-ed. The onboard AMS1117-3.3 LDO gives it a good input power range, from 15V to 5V. And it still has spare, usable CPU pins. Together with its relays and WiFi capability, this makes for a very capable IoT design. Here's my hack (based on Zaschipas) on the 1 relay version of this board.

Indeed the same basic design is used in very many cheap China IoT devices (smart light bulbs, power switches, etc), usually supported by the eWeLink App. The IoT and WiFi portion has already been open-sourced via Tasmota and others, but if you can re-program the N76E003, you can extend or re-purpose it. And even better if you can use open-source programmers and IDE.

Since this is probably a low-use system I aimed for a cheap CPU programmer, something sold as "Nu-Link Simulator Offline Download Function Full Series of N76E003" for RM58. 

Nu-Link Simulator Offline Download Function Full Series of N76E003


This is probably derived from Nuvoton's Nu-Link-Me programmer so the schematics are not likely to be very different.

Nuvoton Nu-Link-Me

The usual software for Nuvoton Nu-Link-Me is the proprietary Keil or IAR. There is a delicious  NuEclipse for Linux but sadly it is for their ARM-based NuMicro series. You can probably run Keil with Wine in Line, but I decided to try erincandescent's nuvoprog.

nuvoprog runs on Go. I used a Debian system as nuvoprog had problems installing on my Slackware machine.  There's the usual Debian preamble:

# apt-get update
# apt-get upgrade

Next I downloaded Go, and installed it:
# tar -C /usr/local -xzf go1.14.4.linux-amd64.tar.gz

# vi /etc/profile

Added /usr/local/go/bin to PATH

# export PATH=$PATH:/usr/local/go/bin

A quick test is in order:

# cat hello.go
package main
import "fmt"
func main() {
  fmt.Printf("hello, world\n")
}

# go build hello.go
# ./hello
hello, world

Now for nuvoprog:
# go get -u github.com/erincandescent/nuvoprog
go: missing Git command. See https://golang.org/s/gogetcmd
package github.com/erincandescent/nuvoprog: exec: "git": executable file not found in $PATH

OK, I need git.

# apt-get install git
root@aspire5050:/home/heong/go# go get -u github.com/erincandescent/nuvoprog
# github.com/karalabe/hid
exec: "gcc": executable file not found in $PATH

And gcc.

#  apt install build-essential

# gcc --version
gcc (Debian 8.3.0-6) 8.3.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

# apt-get install manpages-dev

Now it works:
# go get -u github.com/erincandescent/nuvoprog

Rather inconveniently it was installed in /root:
# ls -l /root/go/bin
total 5184
-rwxr-xr-x 1 root root 5306096 Jul  6 17:22 nuvoprog

I plugged in the Nu-Link programmer and got:
#/root/go/bin/nuvoprog read -t n76e003 dev.ihx
Error: Unsupported device
Usage:
  nuvoprog read [outfile.ihx] [flags]

Flags:
  -h, --help   help for read

Global Flags:
  -t, --target string   target device
  -v, --verbose         make verbose (enable debug logging)

Unsupported device

At least it recognized the programmer. Time to plug in the N76E003 Core Controller board. A little hiccup here: the Nu-Link programmer (and cable) is dual row by 5 way and the Core Controller board is a single row 8-pin header.

Note the programming port on the left and unpopulated J2


A quick check of the pinouts showed that the Nu-Link only used one of its 2 rows. The Core Controller programming port pinouts show that the first 5 pins are compatible with the Nu-Link pinout. So if I soldered a 5-way header into the Core Controller this would fit the Nu-Link cable.

If you orientate the cable connector via its polarizing tab when plugged in correctly overhangs the edge Core Controller.

Check that your Nu-Link voltage is set to 3V3, and just like that, it works:
#/root/go/bin/nuvoprog read -t n76e003 dev.ihx
# ls -l
total 122896
drwxr-xr-x 2 root  root       4096 Jul  6 17:29 bin
-rw-r--r-- 1 root  root      29256 Jul  6 17:48 dev.ihx
-rw-r--r-- 1 heong heong 123711003 Jul  6 17:05 go1.14.4.linux-amd64.tar.gz
-rwxr-xr-x 1 root  root    2080884 Jul  6 17:10 hello
-rw-r--r-- 1 root  root         74 Jul  6 17:10 hello.go
drwxr-xr-x 3 root  root       4096 Jul  6 17:29 src

The resulting file dev.ihx is blank, but then so is my Core Controller board.
# cat dev.ihx | head
:020000040003F7
:08000000FFFFFFFFFFFFFFFF00
:020000040000FA
:20000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00
:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40

For a compiler, I used the tried and true sdcc:

# apt-get install sdcc

Next we want a simple 'Hello, world" toy program for the N76E003 Core Controller. I did not need to look far, for erincandescent has blink_raw.c. Unlike many other sample programs, it only needed one include file, n76e003.h. I downloaded both files to the same directory and simply modified the path to the include file. Also note corrected output pin P15:

# cat blink.c
/* Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */
#include <stdint.h>
#include "n76e003.h"
// 16Mhz clock
#define CLOCK 16000000L
// Divide by 12
#define T0CLOCK ((CLOCK)/12L)
// Per milisecond
#define T0_1MS ((T0CLOCK)/1000L)
static void msdelay(uint32_t count)
{
        uint16_t reload = -T0_1MS;
        // Input = Fsys/12
        SET_FIELD(CKCON, T0M, 0);
        // Mode 1
        SET_FIELD(TMOD,  T0M, 1);
        // Start
        TR0 = 1;
    while (count != 0)
    {
        TL0 = reload      & 0xFF;
        TH0 = reload >> 8 & 0xFF;;
        while(!TF0);
        TF0 = 0;
            count--;
    }
    TR0 = 0;
}
void main() {
        // Set pins in old-skool Quasi Bidirectional mode
        P0M1 = 0;
        P0M2 = 0;
        P1M1 = 0;
        P1M2 = 0;
        P3M1 = 0;
        P3M2 = 0;
        for (;;) {
                int i;
                for (i = 0; i < 10; i++) {
                        P15 = 0;
                        msdelay(1000);
                        P15 = 1;
                        msdelay(1000);
                }
                msdelay(2000);
        }
}
You should also download the config.json file:
# cat ./config.json
{
    "boot_select": "aprom",
    "pwm_enabled_during_ocd": false,
    "ocd_enabled": true,
    "reset_pin_disabled": false,
    "locked": false,
    "ldrom_size": "0kb",
    "bod_disabled": false,
    "bod_voltage": "2v2",
    "iap_enabled_in_brownout": false,
    "bod_reset_disabled": false,
    "wdt": "disabled"
}

It compiles nicely (ignore the warning):

# sdcc blink.c -D FOSC_160000
blink.c:24: warning 158: overflow in implicit constant conversion

# ls -l
total 224
-rw-r--r-- 1 root root 13593 Jul  6 18:40 blink.asm
-rw-r--r-- 1 root root  1428 Jul  6 18:31 blink.c
-rw-r--r-- 1 root root   664 Jul  6 18:40 blink.ihx
-rw-r--r-- 1 root root   242 Jul  6 18:40 blink.lk
-rw-r--r-- 1 root root 42153 Jul  6 18:40 blink.lst
-rw-r--r-- 1 root root 26106 Jul  6 18:40 blink.map
-rw-r--r-- 1 root root  1163 Jul  6 18:40 blink.mem
-rw-r--r-- 1 root root  6756 Jul  6 18:40 blink.rel
-rw-r--r-- 1 root root 42153 Jul  6 18:40 blink.rst
-rw-r--r-- 1 root root 49393 Jul  6 18:40 blink.sym
drwxr-xr-x 2 root root  4096 Jul  6 18:30 include
-rw-r--r-- 1 root root  8337 Jul  6 18:40 n76e003.h

And programs just as nicely with:

# /root/go/bin/nuvoprog program -t n76e003 -c @./config.json -a ./blink.ihx

But there is a problem: the LED did not blink. Luckily mark-fink mentioned that a Core Controller board just like mine had an unpopulated jumper J2 that needed soldering. And, the pin controlling the LED is P15 instead of P12.

Add a jumper (in green) to the Core Controller board for blink_raw.c to work


After a recompile and reprogram the Core Controller blinked nicely. OK so I have a working N76E003AT20 development system. 

Now for the LC Technology Dual Relay WiFi board. The header pinouts were almost identical, except CLK and DAT were reversed. Naughty of them, but never mind: it is just an inconvenience. Another cable should do the trick.
            
            Nu-Link                  2-channel relay
            3V3
            DAT                            CLK
            CLK                            DAT
            RST                            RST
            GND                            GND

Nu-Link programmer wired to Relay Board. Remember to remove the ESP-01S from its socket while programming


The relay board is a 5V device. If you popped the Nu-Link case open, there is a jumper to set the Nu-Link voltage to 5V. Be careful to set it back to 3V3 when using it for the Core Controller though, which takes 3V3 at the programming port.

If we now try to read the N76E003 progran from the relay board it fails:

# /root/go/bin/nuvoprog read -v -t n76e003  ../nuvoton/2channel_relay_readback.ihx
2020/07/06 20:25:22 >  013efffffffffffffffffffffffffffffffffffffffffffffffffffff
fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
2020/07/06 20:25:22 <  0114fd1a00000920010068882f615a01320100002000000064c2a6e91
28d78a54955484b46e6291d69bc03b6c70319896b2b8892674ef446d10e15c0d12a907c
2020/07/06 20:25:22 Setting config {1000 N76E003 3300 0 0}
2020/07/06 20:25:22 >  0218a2000000e803000000080000e40c0000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000
2020/07/06 20:25:22 <  020ca20000000920010068882f615a01320100002000000064c2a6e91
28d78a54955484b46e6291d69bc03b6c70319896b2b8892674ef446d10e15c0d12a907c
2020/07/06 20:25:22 OK
2020/07/06 20:25:22 Performing reset {Auto ICP Mode Ext Mode}
2020/07/06 20:25:22 >  0310e2000000000000000500000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000
2020/07/06 20:25:22 <  0304e20000000920010068882f615a01320100002000000064c2a6e91
28d78a54955484b46e6291d69bc03b6c70319896b2b8892674ef446d10e15c0d12a907c
2020/07/06 20:25:22 OK
2020/07/06 20:25:22 Performing reset {None (NuLink) ICP Mode Ext Mode}
2020/07/06 20:25:22 >  0410e2000000050000000500000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000
2020/07/06 20:25:22 <  0404e20000000920010068882f615a01320100002000000064c2a6e91
28d78a54955484b46e6291d69bc03b6c70319896b2b8892674ef446d10e15c0d12a907c
2020/07/06 20:25:22 OK
2020/07/06 20:25:22 Checking device ID
2020/07/06 20:25:22 >  0508a3000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000
2020/07/06 20:25:22 <  0508a30000005036ff0068882f615a01320100002000000064c2a6e91
28d78a54955484b46e6291d69bc03b6c70319896b2b8892674ef446d10e15c0d12a907c
2020/07/06 20:25:22 OK, Device ID  0x00ff3650
Error: Unsupported device
Usage:
  nuvoprog read [outfile.ihx] [flags]

Flags:
  -h, --help   help for read

Now it does look like the N76E003 is responding, except now the CPU Device ID 0x00ff3650 is not recognized. This will be addressed in Part 2.

So, we have a Linux development system for the N76E003 and showed it to work for the N76E003 Core Controller board, but not for the LC Technology Relay board. 

Happy Trails

Thursday, 2 July 2020

Raspberry Pi 4 Voice Assistant Part 2 of 4: Google Text to Speech




gTTS: The Empire Strikes Back


In Part 1, one of my goals was to have my laptop issue voice commands to to my Google Home smart speaker. After trying out Mycroft, Jasper seems like the logical next step. The other text to speech systems voice quality were something like the Texas Instruments Speak & Spell products: especially ESP32Talkie. Even Mycroft sounded a bit sad next to my Home Mini speaker.

And then I stumbled upon gTTS, Google Text to Speech.  This python interface to Google's text to speech can be installed using:

pip install gTTS

And requires a program, of only ten lines:

from gtts import gTTS

def text_to_speech(input_name, output_name, language):
    file = open(input_name, 'r')
    content = file.read()
    file.close()
    sound = gTTS(text=content, lang=language)
    sound.save(output_name + '.mp3')
    #https://pypi.org/project/gTTS/
text_to_speech('input_en.txt', 'sound_en', 'en')
#text_to_speech('input_tr.txt', 'sound_tr', 'tr')
print('Done!')

You put your text in a file, input_en.txt:
$cat ./input_en.txt
Hey, Google

$python tts.py

And you get back an mp3 file, sound_en.mp3

And all you need to do now to trigger the Google Home smart speaker is:

$mplayer sound_en.mp3

A typical complete command would be something like:
$mplayer HeyGoogle.mp3; sleep 1; mplayer OfficeLampOn.mp3

For some reason, the other trigger phrase 'OK Google' did not work, but for very little effort I can now integrate disparate IoT devices, be they home brewed, Alexa, or Google Home into one Voice Assistant that rules them all.

Here's what it sounds like:



Happy Trails.

Luke: Vader... Is the dark side stronger?

Yoda: No, no, no. Quicker, easier, more seductive.

Sunday, 28 June 2020

More Power! HY-M154 4-Channel Optocoupler PCB as Digital Output

Tim Allen's Home Improvement TV Series

More power always seem like a good idea at first. Well at least you fail in style: if you were to crash and burn, you might as well burn the candle at both ends.

PC817-based 4-channel optocoupler PCB
The HY-M154 is a cheap and cheerful optocoupler board, and is very useful converting digital input voltages to the 3.3V required by the ESP8266 or Raspberry Pi GPIO pins.

Note the 3K series resistors at the input as well as output

You could for instance feed 12V at the input IN1 and this will pass about 4mA into the optocoupler diode. The PC817 has a current transfer ration (CTR) of 50% so the output will put out at least 2mA. A typical V1 is 3.3V so this is more than enough to pull the ESP8266 or Raspberry Pi GPIO pin low.

But what if I wanted to use it as an optoisolated digital output board? If I connected an ESP8266 3.3V GPIO configured as output, it will light up the input LED and feed approximately 1mA into the optocoupler diode. With a CTR of 50% I would expect a maximum of 0.5mA at the output. With 5V at V1, I measured 0.09mA at the output. That will light up an LED dimly, but will not do much else.

One way to improve the output drive is to convert the output to a darlington stage. Indeed, with typically low CTRs, optocouplers with darlington outputs are quite common, like the 6N138.

Optoisolator with darlington output

I have plenty of the venerable 2N2222 in TO-92 package which will attach nicely to the HY-M154:

Darlington output stage using 2N2222 transistor
But this only got me 0.25mA, a gain of just 2.5, which was disappointing because darlingtons can often achieve gains of 100 or more. Now this might be because of the 3K series output resistor. I shorted it with a pair of tweezers and I now get 5.6mA. Much much better: I get a decent brightness at the output LED now, but more power! would be nice. Maybe 100mA, enough to drive a small relay or an ultrabright LED, or a buzzer.

To increase power I would need to drive the optocoupler input diode harder, and the input 3K resistor needs to be changed. I started with a 30R resistor.

HY-M154 with 30R input and 0R output resistors

  I get 39mA at the darlington output at 5V. Now we're talking. Lowering  it further to 10R should be good enough to drive a small relay.

I used this program to blink the ouputs.

Lolin NodeMCU ESP-12E driving HY-M154. Note easyhook supplying 5V to HY-M154 output darlington 

Happy Trails.




Thursday, 14 May 2020

Raspberry Pi 4 Voice Assistant: Mycroft Part 1 of 3


(Shown a photo of a baby)
Mycroft: "Yes, looks very ... fully functioning."

Sherlock: "Is that the best you can do?"
Mycroft: "Sorry, I've never been very good with them."
Sherlock: "Babies?"
Mycroft: "Humans."

My Seeed Studio Respeaker 4-Mic Array arrived during the 2020 Covid-19 Lockdown, which pretty much guaranteed it some immediate attention.




The Seeed Studio link has some good instructions, and it was smooth sailing until the section "Alexa/Baidu/Snowboy SDK". I have Google Home Mini smart speakers and so was loath to register for Alexa. Baidu seemed like a good alternative, for in 2016 Baidu published a stunning paper on Deep Speech on using deep learning on speech to text.

Getting Baidu authorization keys, however proved way too slow so I took a quick look at Mozilla's implementation of Deep Speech, using Google's Tensorflow.

The purpose of all this (besides having some fun) is to see if I can voice-control my IoT devices without an Internet link. Also the added security and privacy seems worthwhile. And it is not like I'm going anywhere for a few days.

At this point Mycroft looks tempting, and since the instructions are straightforward, I downloaded the image file. There is a typo in the image write to sdcard; just replace /dev/sdb1 with /dev/sdb:

sudo dd if=path-to-your-image.img of=/dev/sdb bs=20M

Per the instructions, you will have to register with their website so keep a note of your registration
code on the screen. Keep following until the section "Selecting audio output and audio input".

Respeaker is not listed in the microphones' list, But Dimitry Maslov comes to the rescue:

sudo apt-get update
sudo apt-get upgrade
git clone https://github.com/respeaker/seeed-voicecard.git
cd /home/pi/seeed-voicecard
./install.sh 4mic

Next, go back to Mycroft: a quick and clean way is to reboot. Reconfigure it again with

mycroft-setup-wizard

And select 'Other'. Mycroft should now work. Here's a video of mine. 


Mycroft/Picroft on Raspberry Pi 4 and Respeaker 4-mic Array


Mycroft seems to run a lot slower than Google Assistant. This is because it also uploads the audio to cloud servers and Mycroft servers probably have a lot less oomph.

Next we want Mycroft to turn on an IoT lamp. We can used a few services for this, for example, Adafruit but for simplicity we can use an esp8266 1-channel relay and a webhook. We use 'mycroft-msk create' and fill in the questionnaire:

(.venv) pi@picroft:~ $ mycroft-msk create
Enter a short unique skill name (ie. "siren alarm" or "pizza orderer"): soldering station lamp

Class name: SolderingStationLampSkill
Repo name: soldering-station-lamp-skill

Looks good? (Y/n) n
Enter a short unique skill name (ie. "siren alarm" or "pizza orderer"): soldering station lamp on

Class name: SolderingStationLampOnSkill
Repo name: soldering-station-lamp-on-skill

Looks good? (Y/n) y
Enter some example phrases to trigger your skill:
- Soldering station lamp on
- Soldering station light on
- Turn on the soldering station lamp
- Turn on the soldering station light
-
Enter what your skill should say to respond:
- The soldering station light is now on
- Turning on the soldering station light
-
Enter a one line description for your skill (ie. Orders fresh pizzas from the store):
- Turns on the light on the soldering station
Enter a long description:
> Turns on the light on the soldering station
>
Enter author: cmheong
Go to Font Awesome (fontawesome.com/cheatsheet) and choose an icon.
Enter the name of the icon: lightbulb
Pick a color for your icon. Find a color that matches the color scheme at mycroft.ai/colors, or pick a color at: color-hex.com.
Enter the color hex code (including the #): #fff68f

Categories define where the skill will display in the Marketplace. It must be one of the following:
Daily, Configuration, Entertainment, Information, IoT, Music & Audio, Media, Productivity, Transport.
Enter the primary category for your skill:
- IoT
Enter additional categories (optional):
-
Enter tags to make it easier to search for your skill (optional):
- IoT
- Smart Home
- Home Assistant
-
For uploading a skill a license is required.
Choose one of the licenses listed below or add one later.

1: Apache v2.0
2: GPL v3.0
3: MIT
Choose license above or press Enter to skip? 3

Some of these require that you insert the project name and/or author's name. Please check the license file and add the appropriate information.

Does this Skill depend on Python Packages (PyPI), System Packages (apt-get/others), or other skills?
This will create a manifest.yml file for you to define the dependencies for your
 Skill.
Check the Mycroft documentation at mycroft.ai/to/skill-dependencies to learn more about including dependencies, and the manifest.yml file, in Skills. (y/N) y
Would you like to create a GitHub repo for it? (Y/n) y

Enumerating objects: 12, done.
Counting objects: 100% (12/12), done.
Delta compression using up to 4 threads
Compressing objects: 100% (10/10), done.
Writing objects: 100% (12/12), 2.52 KiB | 322.00 KiB/s, done.
Total 12 (delta 0), reused 0 (delta 0)
To https://github.com/cmheong/soldering-station-lamp-on-skill
 * [new branch]      master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.
Created GitHub repo: https://github.com/cmheong/soldering-station-lamp-on-skill
Created skill at: /opt/mycroft/skills/soldering-station-lamp-on-skill

And that is all there is to it. Don't worry about uploading to github - it is optional. You will now get a whole bunch of smallish files:

(.venv) pi@picroft:~ $ ls -l /opt/mycroft/skills/soldering-station-lamp-on-skill
/
total 32
-rw-r--r-- 1 pi pi  393 May 14 15:15 __init__.py
-rw-r--r-- 1 pi pi 1058 May 14 15:20 LICENSE.md
drwxr-xr-x 3 pi pi 4096 May 14 15:13 locale
-rw-r--r-- 1 pi pi 1009 May 14 15:20 manifest.yml
drwxr-xr-x 2 pi pi 4096 May 14 15:15 __pycache__
-rw-r--r-- 1 pi pi  531 May 14 15:17 README.md
-rw-r--r-- 1 pi pi   35 May 14 15:17 settings.json
-rw-r--r-- 1 pi pi  631 May 14 15:20 settingsmeta.yaml

The file we are interested in is __init__.py. Since this is a toy example to get you going, we are going to use the crudest possible and most insecure method, using a bash shell to launch our webhook. The modified file is in my github repository, but it is so small I'll also list it here:.

$cat __init__.py
from mycroft import MycroftSkill, intent_file_handler
import subprocess

class SolderingStationLampOn(MycroftSkill):
    def __init__(self):
        MycroftSkill.__init__(self)

    @intent_file_handler('off.lamp.station.soldering.intent')
    def handle_off_lamp_station_soldering(self, message):
        cmd = "curl -k http://ww.xx.yy.zz:8080/1/on"
        answer = ""
        try:
            answer = subprocess.check_output(cmd, shell=True)
        except:
            print(str(answer))
        print(str(answer))

        self.speak_dialog('off.lamp.station.soldering')


def create_skill():

    return SolderingStationLampOn()

Here's a video of the result. You will notice there is another skill to turn off the lamp.

Mycroft with IoT Skil

But what I really wanted was Mycroft to function offline. There is some talk of a "Personal Server" version, but as this forum shows, there is a lot of code by the redoubtable JarbasAI, but is not quite ready yet. 

So, it is back to my Respeaker and DeepSpeech image: we will look at Jasper in Part 2.

Happy Trails

Friday, 8 May 2020

Hacking the Raspberry Pi Model B to use with Geekworm UPS Hat


Raspberry Pi Model B (not Plus) with Composite Video socket dismounted
I recently bought a Geekworm UPS Hat for the Raspberry Pi Models B Plus and later. It has, shall we say a few rough edges. Especially running loads of 500mA and above. It often functions as a UPS at loads of say 480mA. If I paired it with a say, Raspberry Pi Model B, it works quite well.

Now I have always wanted a solar-powered wifi repeater. In the day, a solar panel provides DC power and also charges up an NS60 (nominal 60Ah at 12v) car battery. At night it runs off the battery. It seems reasonable enough: the Raspberry Pi 3 Model B Plus wifi repeater took up more than 500mA at 5V 24 hours a day, whilst the solar panel might supply 2A at 19V for maybe 6 hours a day.

But before I could buy the NS60, the Covid-19 pandemic of 2019 intervened. Rather than wait for lockdown to pass, why not reconfigure it to run from solar power in the day, and seamlessly switch over to mains power at night. I would need a couple of relays: one each for mains and solar power 5V DC-DC buck converters. And to ensure a trouble-free switchover, a UPS Hat for the Raspberry Pi would be nice ...

The Geekworm UPS Hat worked well at 500mA, and misbehaved over 600mA: the WiFi Repeater would reset on switchover. Or it would not charge the lithium battery on switching back to mains power. Now this is actually self-recovering: on the battery running down it would reset the load and the battery would charge again. There might be a minute of WiFi repeater service interruption. TM Net my service provider certainly does that a few times a day. But this is humiliating; not tolerable for anyone other than TM Net.

My Raspberry Pi Model B Plus drew 510mA clean and 600mA once the WiFi dongle started firing up. On the other hand a Raspberry Pi Model B drew only  440mA and might just work. The trouble is, the Geekworm UPS Hat has a 40-pin socket and the Model B only has a 26-pin header.

Raspberry Pi Model B's 26-pin Header

Many hardware designers seek backward-compatibility when upgrading their designs. Often old hats will work on new models, but new hats will not. But this means the headers will have a lot of similarity. Sometimes enough to work. A quick comparison shows that the first 26 pins of the 50-pin header is pretty much identical, except for GPIO 19-21. The I2C pins are the same, and crucially, so are the power and ground pins.

Raspberry Pi Model B Plus' 40-pin Header
And as long as the 3 contentious GPIO pins are not used, they will default to GPIO input, and inputs when mis-wired and unused are harmless.

But there is another problem: the Model B's composite video output, an RCA socket is too tall and gets in the way of the Geekworm 40-pin socket. This is easily de-soldered.

Remember, an electronic engineer's favorite programming language is solder!
Once the RCA socket is removed, the Geekworm UPS Hat mounts nicely onto a Model B.

Geekworm UPS Hat on Raspberry Pi Model B
The Model B powers on nicely from battery. But the proof of the pudding is in the eating. Besides the Geekworm UPS I2C device at address 0x48 I also had an ADS1115 4-channel analog input card at address 0x36.

# i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- 36 -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- 48 -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --             

And from my last post, 

# ./ups_read
97

# ./ups_read -vc
4.161250V 96.550781%


Blinkenlights galore: Raspberry Pi Model B booting from battery power

To probe it a little further, I printed out a few more registers of the MAX17048:
# ./ups_read2 -a
MODE 10 00
CFG 97 1c
CRATE ff f9
VER 00 12
STAT 00 ff
VRST 00 0c
VCELL cf f0
CREG 60 65
4.158750V 96.394531%

The code is in my github repository.

Note that the 5V input into the Geekworm UPS Hat greatly affects its operation. In the picture of the WiFi extender below, a smaller 5V DC-DC buck converter (red PCB) was used, and this resulted in the Geekworm Hat not charging. Swapping it out for a 5V 3A unit (blue PCB) did the trick. Note that when the battery is at full charge, it takes a little while, maybe a few minutes, for the Hat to start charging.

Raspberry Pi Model B with Geekworm UPS Hat, installed as daytime solar powered WiFi extender . From top: ADS1115 I2C analog converter, WiFi dongle, Pi Model B with Geekworm UPS Hat, 2A buck converter(disconnected), and 3A CC CV buck converter

And yes, the Raspberry Pi daytime solar WiFi Repeater works for now. It's early days yet and there are many switchovers and switchbacks yet to come.

And there you have it: how to hack a 26-pin Raspberry Pi Model B for the Geekworm UPS Hat.

Happy Trails