computing

Solar Cluster: Load test using the power controller

So, last night I started doing some light testing of the power controller. I installed a 5A fuse and hooked it up to a 10Ah LiFePO₄ battery and a homebrew 3A charger (LM2576-based with a 16V IBM laptop PSU as mains source) set to its maximum voltage (~15V).

The controller came to life and immediately started flashing its “high voltage” and “low temperature” LEDs, indicating that:

  • It thought the temperature was high enough to warrant a fan turning slowly (above ~20°C)
  • It thought the battery voltage was too high for comfort (IPMI complains when the voltage gets much about 13.6V.)

In order to get the battery to discharge, I plugged in an old Icom IC-706MkII G transceiver, set it on receive mode. I didn’t have an antenna attached, so this would have represented a very light load for what would be production use.

The battery started discharging, and after a few tens of minutes, the high voltage warning LED had stopped flashing and instead the “good voltage” LED was staying constantly on. So battery was in a range the controller was happy with.

It was going to take a long time though, for that set to drain a 10Ah battery in receive mode.

This morning, I got out the big guns. I plugged the actual cluster in and fired it up. After about 30 minutes of run time, the battery had drained sufficiently that the charger started flashing the “good voltage” LED, indicating battery was getting low. It had also turned on the MOSFET controlling the charger I had plugged in, and the charger was desperately trying to keep up with the ~5A load that the cluster was drawing. (Did I mention this was a 3A charger?)

So far so good. I powered off the cluster and unplugged it. It continued to let the charger do its job, and after another short while, the battery had regained some charge. It kept the charger on until momentarily, it peaked over the “high voltage” threshold. The “high voltage” LED blinked a few times, then the MOSFET turned off and the “good voltage” LED remained solid.

The battery was sitting at 13V. A little short of my 13.5V set-point, but close enough given it was on a fairly weak charger. So that ATTiny24A is doing exactly what I intended.

Maybe the high set-point could do with some adjustment, or a turn-off delay added (with a separate “high critical” set-point for immediate shut-off), but so far, so good.

It might be worth me getting some PCBs fabricated and shrinking the prototype board down using SMD parts, but this controller works so far.

Solar Cluster: Power controller firmware taking shape

So after a long hiatus, some of it involving some yak shaving (e.g. #Open-source debugWire debugger yes, I’ll get back to that), I managed to get a version of the firmware together for the power controller that seems to be doing what I ask of it.

The means of overcoming the road block was knocking up a very crude (and slow!) UART driver so I could print data out on the serial port. I avoided doing this previously because I didn’t have an easy way to interface to a TTL serial port. Recently though, I bought some FTDI serial cables, one 5V and one 3.3V, so now I had little excuse.

I feel these will give me some valuable insights into tacking the debugWire project.

I was able though, to bit-bang a UART using avr-libc’s _delay_us, and get a respectable 4800 baud serial stream out. This obviously dropped to 300 baud when I had other tasks running, but still, that’s enough to do what I’m after. (Once upon a time, that was considered fast!)

After figuring out where I was going wrong… perhaps I had been sniffing too much solder smoke that day… I re-wrote my firmware, using this UART library as a means of debugging the code. I set up Timer1 to run at 1.2kHz, which meant I could also use it as a baud rate generator for my software UART and upping the baud rate to 1200bps.

Some further work on a breadboard, and I had more-or-less working firmware.

I’ve thrown the code up on GitHub, it’s very much in a raw state, and I might do a second revision of the PCB, since this prototype seems to be more or less on the money now.

Song-book for the spy agencies

Seems spying on citizens is the new black these days, most government “intelligence” agencies are at it in one form or another. Then the big software companies feel left out, so they join in the fun as well, funneling as much telemetry into their walled garden as possible. (Yes, I’m looking at you, Microsoft.)

This is something I came up with this morning. It’s incomplete, but maybe I can finish it off at some point. I wonder if Cortana has a singing voice?

Partial lyrics for the ASIO/GCHQ/NSA song book

Aliasing variables in gcc

A little trick I just learned today. First, the scenario.

I have a driver for a USART port, the USART on the ATMega32U4 in fact. It uses a FIFO interface to represent the incoming and outgoing data.

I have a library that also uses a FIFO to represent the data to be sent and received on a USART.

I have an application that will configure the USART and pipe between that and the library.

Now, I could have each component implement its own FIFOs, and have the main application shovel data between them. That could work. But I don’t want to do this. I could have the user pass in a pointer to the FIFOs in initialisation functions for the USART driver and the library, but I don’t want to store the extra pointers or incur the additional overheads.

Turns out, you can define a symbol somewhere, then alias it to make two variables appear in the same place. This is done with the alias attribute, and it requires that the target is defined with the nocommon attribute.

In the USART driver, I’ve simply declared the FIFOs as extern entities. This tells the C compiler what to expect in terms of data type but does not define a location in memory. Within the driver, use the symbols as normal.

/* usart.h */

/*! FIFO buffer for USART receive data */
extern struct fifo_t usart_fifo_rx;

/*! FIFO buffer for USART transmit data */
extern struct fifo_t usart_fifo_tx;

/* usart.c */
static void usart_send_next() {
  /* Ready to send next byte */
  int16_t byte = fifo_read_one(&usart_fifo_tx);
  if (byte >= 0)
    UDR1 = byte;
}

ISR(USART1_RX_vect) {
  fifo_write_one(&usart_fifo_rx, UDR1);
}

I can do the same for the protocol library.

/* External FIFO to host UART */
extern struct fifo_t proto_host_uart_rx, proto_host_uart_tx;

/*! External FIFO to target UART */
extern struct fifo_t proto_target_uart_rx, proto_target_uart_tx;

Now how do I link the two? They go by different names. I create aliases, that’s how.

/*
 * FIFO buffers for target communications.
 */
static struct fifo_t target_fifo_rx __attribute__((nocommon));
static uint8_t target_fifo_rx_buffer[128];
extern struct fifo_t usart_fifo_rx __attribute__((alias ("target_fifo_rx")));
extern struct fifo_t proto_target_uart_rx __attribute__((alias ("target_fifo_rx")));

static struct fifo_t target_fifo_tx __attribute__((nocommon));
static uint8_t target_fifo_tx_buffer[128];
extern struct fifo_t usart_fifo_tx __attribute__((alias ("target_fifo_tx")));
extern struct fifo_t proto_target_uart_tx __attribute__((alias ("target_fifo_tx")));

/*
 * FIFO buffers for host communications.
 */
static struct fifo_t host_fifo_rx __attribute__((nocommon));
static uint8_t host_fifo_rx_buffer[128];
extern struct fifo_t proto_host_uart_rx __attribute__((alias ("host_fifo_rx")));
static struct fifo_t host_fifo_tx __attribute__((nocommon));
static uint8_t host_fifo_tx_buffer[128];
extern struct fifo_t proto_host_uart_tx __attribute__((alias ("host_fifo_tx")));

Now a quick check with nm should reveal these to all be at the same locations:

RC=0 stuartl@vk4msl-mb ~/projects/debugwire/firmware $ avr-nm leodebug.elf \
     | grep '\(proto_.*_uart_.x\|host_fifo_.x\|target_fifo_.x\)'
0080022c b host_fifo_rx
008001ac b host_fifo_rx_buffer
0080019c b host_fifo_tx
0080011c b host_fifo_tx_buffer
0080022c B proto_host_uart_rx
0080019c B proto_host_uart_tx
0080034c B proto_target_uart_rx
008002bc B proto_target_uart_tx
0080034c b target_fifo_rx
008002cc b target_fifo_rx_buffer
008002bc b target_fifo_tx
0080023c b target_fifo_tx_buffer

Solar Cluster: Debugging an ATTiny24A, fun and games with SPI

So, the debugging saga continues.

I’ve been busy the last few weeks so haven’t had much time to look at this, but I did get back to it today, and had some quality time with the Raspberry Pi AVR programmer and the ATTiny24A.

I ended up making a programming jig for the ATTiny24A in fact, so I could use it on a bare breadboard. I basically took a 14-pin DIP IC socket, soldered some header pins to it and tacked wires onto the pins that go to the ISP header and wired those out on a 2×6 pin header. So the socket basically plugs straight into a breadboard and I’ve got a loose ISP header that the Pi plugs into.

All built with recovered parts from old motherboards. 🙂

I was able to connect nearly every pin to a LED, so could then see everything that was going on the SPI bus, and could use the remaining pins to indicate status. So I’m now starting to understand the USI device a bit better, but still struggling to get meaningful data.

A big challenge is synchronisation. SPI normally uses a chip-select pin, pulling this low can trigger the MCU to prepare for a command on SPI. The 6-pin ISP interface does not provide this however, so I have to kludge it. I find the Pi, when it stops transmitting, it lets go of SCK and it floats up ever so slightly, causing the MCU to think another pulse has started. Once that happens, the real games begin as the two are then out of step, never to recover.

I experimented today with using the 16-bit timer to count down a delay — basically it’s the Modbus/RTU approach, whereby if there’s a break in the comms, we reset.

Co-ordinating this though is a major headache.

I’ve looked into using debugWire. This apparently uses a UART-based protocol running over an open-drain connection through nRESET. My RPi-AVR programmer in theory could do it, but I’d need to bit-bang the UART as my reset GPIO is not one of the hardware UART pins. There is a driver out there.

The other option is to consider the LeoStick I mentioned in a previous post. This evening, I stumbled on the specs for the JTAGICE mkII protocol, so maybe I could develop my own debugger based on this protocol and use avarice? Sounds like a new project.

Another prospect is to say f### it and just use the LeoStick as my power controller. For that matter, the Adafruit Pro Trinket is cheap and uses a similar MCU. (ATMega328 instead of ATMega32U4.) Decisions, decisions…

Solar Cluster: Debugging an ATTiny24A, programmer upgrade

So, debugging the ATTiny24A, one big problem I’ve got is understanding what the ADC is seeing in each channel. There’s no serial output, no LCD, just a handful of LEDs and a PWM output. Not good enough.

The ICSP header though, necessarily exposes the pins needed to do SPI and I²C. Could that do? I’d need something to do the transfers with.

The programmer I’ve used to date has been a Olimex STK500v2 clone (the tiny one built into a DB25 backshell), which works well, but it has one nit: I haven’t figured out a way to do raw SPI transfers with it. It might be possible, I’m not sure.

I immediately thought of the Raspberry Pi. The other option I had close on hand was a Freetronics LeoStick. One I’d have to write programming firmware for — which may be worth doing some day. The other, I can just install from repositories. But how does one interface the two?

Adafruit have this tutorial on doing exactly that. HOWEVER, they wire the Pi straight up to the AVR. Fine if they’re both 3.3V, but trouble if the AVR is running at 5V like mine. I’d expect this to release magic smoke!

So, a level shifter is needed. I happened to have a Freetronics one laying around which gave me 4 channels, good enough. I just had to figure out what pins to use. For reasons unexplained, Adafruit seem to pick weird and wonderful pins that are not close together. Another guide, suggested using the standard SPI pins. I more or less went this route, but used GPIO channel 22 instead for reset, so I could use the one female header to connect to them all.

The connector was a spare that came with the LeoStick: they come with two 13-pin ones. I cut it with a hacksaw to give me two 3-pin headers and a 6-pin header. The 3-pin headers were glued together to give me a 2×3 pin header, and the other was soldered to the level converter. Two pins had to be swapped, annoyingly, but otherwise wiring it up was straightforward.

I just ran some off-cut CAT5e cable to the ICSP connector, keeping the lead length short so as to prevent clock skew.

The configuration file for AVRDude looks like this:

# Linux GPIO configuration for avrdude.
# Change the lines below to the GPIO pins connected to the AVR.
programmer
  id    = "pi";
  desc  = "Use the Linux sysfs interface to bitbang GPIO lines";
  type  = "linuxgpio";
  reset = 22;
  sck   = 11;
  mosi  = 10;
  miso  = 9;
;

I can flash my ATTiny24A from the Pi now with the following command:

$ sudo avrdude -p t24 -c pi …arguments…

So with that done, I should be able to use a simple Python script to read and write bytes via bit-banged SPI via the ICSP header, and implement some firmware to react via SPI.

Solar Cluster: More MOSFET fun

So I’ve managed to get the board up and going, sort-of. I’m developing the firmware, getting acquainted with the ATTiny24A’s hardware.

The logic is that it’ll be sensing the battery voltage and two inputs from mains and solar, and so when it goes into charge mode, it picks one of the two sources and starts pumping current. Simple enough.

Except testing it has proven “fun”. I hooked everything up, using a power supply with a diode to stand in for the battery. I noticed the ADCs were seeing a voltage on their inputs. How? Why? Of course, the answer was plain to see in the datasheet if I bothered to look!

That little body diode, was of course, passing the current from my “battery” back to the outside world, and that was messing with measurements.

Great. So I’ll be needing a series diode to cram in there somewhere, and the MOSFET is expected to switch up to 30A, so the diode needs to handle that too. The challenge, is there isn’t much room for a heatsink.

Actually, the MOSFETs can do over 70A, so I’ll aim for a diode that can do about 60A, with a view that it won’t be stressed doing 30A even without the heatsink. The Vishay VS-60EPU02PBF is looking like a good option, although expensive.

One annoyance is there doesn’t seem to be a diode that has the cathode connected to the tab of a TO-220, as then I’d just solder the MOSFETs and diodes back-to-back and clamp a heatsink to the pair of them.

I guess for now I can try a few experiments to get acquainted with how it’ll all work, perhaps de-solder the tabs of the MOSFETs (again) and perhaps put a small 3A diode in as a stand-in for testing so I can at least get the firmware written.

Solar Cluster: Lessons learned

  1. Don’t rely on the internal pull-up in the MCU for the nRESET pin. It might work for no-connect scenarios, but it’s not going to win a war against a Olimex programmer dongle that’s decided to lean on the pin a bit too hard.
  2. The linker needs to know what MCU it is too.

I basically found I had the ATTiny24A resetting repeatedly when the programmer was connected. Set a LED to stay on, it’d blink. The cause was the programmer was interacting with the MCU via the reset pin when connected. The solution was a resistor between 5V and reset, I stuck a 47k across the relevant pins of the ICSP header. (Annoyingly, they’re at opposite corners!)

The other blooper was having me scratching my head every time I tried to define an interrupt, the MCU would just sit there. Even if the ISR had nothing in it, and sei was never called, it’d still sit there dumb.

My Makefile at this point looked like this:

CFLAGS = -Os -g -mmcu=attiny24a -Wall -Werror
CPPFLAGS = -DF_CPU=1000000UL
CROSS_COMPILE ?= avr-
CC = $(CROSS_COMPILE)gcc
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
SIZE = $(CROSS_COMPILE)size
PROG_ARGS ?=-c stk500v2 -P /dev/ttyACM0
PROG_DEV ?= t24

.PHONY: clean all

all: powerctl.ihex

clean:
	-rm *.ihex *.elf *.o

%.ihex: %.elf
	$(OBJCOPY) -j .text -j .data -O ihex $^ $@

%.elf:
	$(CC) -o $@ $^
	$(SIZE) -d $@
	$(OBJDUMP) -xdS $@

powerctl.elf: powerctl.o
test.elf: test.o

%.pgm: %.ihex
	avrdude $(PROG_ARGS) -p $(PROG_DEV) -U flash:w:$^:i

The call to objdump was to try and figure out what was going wrong. Note that I don’t pass -mmcu to the final link. The linker needs to know what MCU it is just as much as the compiler does. That was my error. Having done this, I now have working interrupts.

Solar Cluster: Charge Controller description

So, I’ve built the controller. The design was pretty simple. Using an ATTiny24A, I’d monitor the voltages of the battery and two power inputs, and code would decide which input to use, if any. It also could use the in-built temperature sensor to control cooling fans. This is the schematic I knocked up this morning.

The values of most resistors are not critical. I found I needed 1kOhm resistors into the bases of the transistors as the MCU was not happy driving them directly. The transistors I’m using are BC547Bs controlling AUIRF4905 MOSFETs.

The only components that are critical are the voltage dividers on the ADC inputs. I’ll be using the built-in 1.1V reference in the MCU as that’s what’s needed for the temperature sensor anyway.

This was a bit of an exercise in reviving old brain cells as it’s been some time since I’ve done a proper PCB myself. This is a one-off prototype with mostly larger components, so no point in getting boards fabricated. I did it the old fashioned way, using a dalo pen then etching in a bath of Ferric Chloride.

That gives you an idea of what the board looked like prior to population. The underside was covered with tape to prevent it from being etched. It took a while, and I think I could have upped the concentration of the solution a bit, since it did leave some tracks un-etched.

Perhaps my solution is getting a little old too… the logo on the bottle really dates it. I found I had to attack the gaps between some tracks with a knife since the etchant didn’t quite get it all.

There are no tracks on the bottom, it’s just one piece of un-etched copper, to act as a ground plane. I guess the construction style is a cross between Manhattan and groundplane (dead-bug) construction. The constructed board looks like this.

I’m not sure what all the LEDs will be doing at this point. Three share pins with the ICSP header, which means they flash as the board is being programmed… useful for troubleshooting ICSP issues. The IC socket is a cheap 14-pin one, I just bent the pins to mount it flush to the board. The 10uF tantalum on the output of the 5V PSU is possibly a 10V one. Where the electrolytic is, is where I had the 330uF tantalum mounted, and it went bang when I gave it 12V.

I tried the following program on the board which just steps through all the LEDs and MOSFETs:

/* board.h */
/* LEDs */
#define LED_U1_BIT		(1 << 7)
#define LED_MOSI_BIT		(1 << 6)
#define LED_MISO_BIT		(1 << 5)
#define LED_SCK_BIT		(1 << 4)
#define LED_U0_BIT		(1 << 3)
#define LED_PORT		PORTA
/* MOSFETs */
#define FET_MAINS		(1 << 0)
#define FET_SOLAR		(1 << 1)
#define FET_FAN			(1 << 2)
#define FET_PORT		PORTB
/* test.c */
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdint.h>
#include "board.h"
uint8_t heartbeat = 10;
int main(void) {
	DDRA = LED_U1_BIT | LED_MOSI_BIT | LED_MISO_BIT
		| LED_SCK_BIT | LED_U0_BIT;
	DDRB = FET_MAINS | FET_SOLAR | FET_FAN;
	PORTA = 0;
	PORTB = 0;
	/* Test sequence */
	while (1) {
		PORTA = LED_U0_BIT;	_delay_ms(1000);
		PORTA = LED_U1_BIT;	_delay_ms(1000);
		PORTA = LED_MOSI_BIT;	_delay_ms(1000);
		PORTA = LED_MISO_BIT;	_delay_ms(1000);
		PORTA = LED_SCK_BIT;	_delay_ms(1000);
		PORTA = 0;
		PORTB = FET_MAINS;	_delay_ms(1000);
		PORTB = FET_SOLAR;	_delay_ms(1000);
		PORTB = FET_FAN;	_delay_ms(1000);
		PORTB = 0;
	}
	return 0;
}

That seems to prove the hardware is alive, and now I just have to get the software working. Now to try out the toolchain I built!

 

Solar Cluster: Working around build errors in crossdev

I’ve been doing further work on my charge controller. Last weekend, I managed to fix the issues that were causing the MOSFETs to not work, and the “faulty” MOSFET turned out to be fine: the fault was a glitch with my home-fabricated PCB. (No, commercial PCB makers, this is not an invitation for you to advertise as you have done on other projects! The job is done.)

In the midst of doing this, I was also having problems getting the toolchain to generate code correctly, the code would fail to run if I had an interrupt service routine defined, even if the interrupts were not enabled it still failed to do anything. Something in the interrupt vector table was off.

So back to crossdev to see if I can build a newer toolchain that works. crossdev -t avr would get as far as building the full C/C++ compiler, then crap out with a missing ldscript. (I don’t have the log handy to show you the exact error, but it takes place in a ./configure script, so you see “C compiler cannot generate executables” or some such like that, and will see the ldscript error in the offending config.log.)

The kludge?

# cd /usr/avr/lib
# ln -s ../../lib64/binutils/avr/${BINUTILS_VERSION}/ldscripts/

Substitute ${BINUTILS_VERSION} with the active version. Voila, having done that kludge, I now have this:

$ avr-gcc --version
avr-gcc (Gentoo 5.3.0 p1.1, pie-0.6.5) 5.3.0
Copyright (C) 2015 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.

Not sure if it works or not, but I’m documenting the above as a note to myself next time I hit this error.