MID400 Line Sensor

So sorry about the long break in updating!

I’ve had a go at implementing the MID400 Line Voltage Sensor from Fairchild Semiconductor. The chip works as expected and really requires a minimal implementation. Basically I’d imagine three of these chips on the final board that will detect AC line levels from the main switches within Silvia. I’d imagine that in the final implementation, I’ll have ganged the output from each of the Quick Connect tabs so that we don’t have to worry about rewiring etc. (i.e., I’ll make a Male-Female-Female gang with the second Female tail connecting to the Duetto via a Quick Connect tab).

I suppose now the following pieces are outstanding: a plastic case / shell for the display, schematizing the final board, and finishing up the first major revision of the Duetto Firmware.

I’m really wrestling with how to implement an effective PID algorithm based on an input in one unit (in this case degrees Centigrade) and outputing a control value in a different unit (in this case, PWM value based on 15.625 kHz). So if anyone has any ideas – feel free to comment below!

This upcoming month is crazy for me with school, so it might very well take me a while before I’m able to dedicate any time to the Duetto project. Hang tight! I’m on it!

A Display That Doesn’t Press My Buttons …

I suppose this is a bit of a blessing in disguise but there are still a significant number of issues with the New Haven Displays. Recently, I sent the Version 2 displays back to New Haven to be reprogrammed with an updated firmware designed to eliminate flickering issues with the backlight. While the new firmware eliminates the flicker, it has also introduced a crippling bug in the displays ability to communicate properly – at least by I2C. For those who are interested in the details, the firmware does not handle a bus line release properly and appears not to be able to handle multi-byte transactions (such as in a custom-character write). Because there is currently no solution – nor is it my desire to debug their firmware – I have decided to go with a different display manufacturer.

I came across the CrystalFontz CFA533-TMI series of displays that provide the same 16×2 White on Blue display – with the added benefit of a built-in keypad. This is great as it frees up a significant number of pins on the Arduino, and eliminates the need for me to implement some sort of key-polling / response in the final software.

The price however is double. Moreover, the standard Arduino LCD libraries are not compatible with this display module. Whereas communication with most displays are unidirectional, communication with the CFA533 module is bi-directional. That is to say that you can send a command and then expect a response from the display (such as indicating the keys that are currently pressed, the keys that have been pressed since the last poll, and the keys that have been released since the last poll). The display also uses a CRC checksum after each command and with each response making communication a slightly more complicated (but less error prone) process.

To my knowledge, a C library or Arduino compatible library has not yet been written that supports this display – so I’ve written one myself. At present, the command set is limited to basic functions like clearing the display, writing text to the display, programming characters, and reading button states. I will release an alpha of the library in due time – when I’ve implemented enough of the command-set to be of use to others.

My initial response to the display module is that it’s great. It packs a fantastic feature set so the cost / benefit is worth it in my opinion.

More to come soon when I implement the Line Voltage sensors to detect Switch states. Oh my!

A Flicker of Hope!

I noticed a marked flicker in the Newhaven Display. It turns out, it’s a glitch in the firmware based on any active communication with the display. Luckily, Newhaven Display acknowledges this, and is willing to exchange / upgrade the firmware. Thank goodness!

I’m at a point where things are really starting to come together piece-wise. I have a few things left to test, including the MID400 line sensors to detect the rocker-switch status’. So far, I have the ADS1247 analog-to-digital converter, LCD display, DS3231M real-time clock, and solid-state relay integrated together. Theoretically, it could operate as an effective PID. But of course, I’m looking to implement much more before I put it to practice.

I’m also fussing around a bit with the code – trying to get everything neat and tidy as opposed to the rats nest that I’ve been using. I’m toying with different looks on the display and how to implement a lean yet effective visual menu.

You can see on the display, the first line is dynamic and displays the real-time clock, boiler status and current temperature (I’ve got a resistor in place to simulate the PT100). The second line of the display shows the custom characters that I’ve designed so far – the thermometer, “Brew”, “Hot Water”, and “Steam” icons. I intend for the latter three to activate as appropriate depending on the switch status.

I’m imagining two modes within the system – Automatic or Manual. In automatic mode, the system would wait for the brew switch to be thrown and then activate the pump. The system would then toggle the pump to control pre-infusion and extraction as programmed. In this mode, the three-way solenoid valve would remain engaged during the pre-infusion to prevent the pre-infusion from back-flushing through the solenoid valve. The solenoid valve disengages when the brew switch is turned off. In manual mode, the system will wait for the brew switch and engage the pump until the switch is turned off. Throwing the hot-water switch has much the same effect in both modes, except that the solenoid valve is not engaged. Switching temperature on the controller to a “steam” temperature (greater than 120°C) and throwing the steam switch will automatically pulse the pump to fill up the boiler. Of course there will be a number of programmable presets so that you can tailor pre-infusions and brew temperatures (or even tea temperatures!). These presets will be able to be set by “learning” how you operate the controls.

It might be a bit before I get the code worked out, but I suppose a few extra espressos might keep me on the ball.

Time and the DS3231M

Because the Arduino Pro Mini lacks the ability to accurately maintain time over extended periods of time, and because of the complexities of time-keeping, I decided to implement an external real-time clock (RTC). Ultimately I want Silvia to turn herself on, and warm up to temperature long before I rise and shine in the morning. I decided to go with an RTC chip produced by Maxim / Dallas Semiconductors – the DS3231M. This is a tiny 8-pin SOIC chip with onboard resonator that can communicate via I2C. As the display also uses I2C, the addition of this little chip on the I2C bus requires minimal effort. Of importance, this chip automatically performs carryover for seconds, minutes, hours, days, weeks, months, years, and century, and supports 24 and 12-hour timekeeping. Literally, you simply have to set it, and forget it. I was able to obtain four of these chips through the Dallas / Maxim sample program – plenty of chips to experiment with and place in the final prototype.

I’ve got both the display and RTC wired up to the Pro Mini – so far, everything is great!

To get the devices up and running together, I use the following skit:


#include <Wire.h>
#include <LCDNHDI2C.h>

//Custom Display Characters
const uint8_t custChar0[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; //Blank Char

uint8_t timeH, timeM, timeS;

LCDNHDI2C LCD = LCDNHDI2C(2, 16, 0x50>>1);
uint8_t lcdBuffer[2][17], lcdBufferCount;
unsigned long dispUpdateInterval; //Number of millis that must pass before the display is updated

void setup()
{
//Turn on I2C Bus
Wire.begin();
LCD.init();
LCD.setCharacter(0, custChar0);
LCD.setBacklight(0x0);
LCD.setContrast(0x2D);

Wire.beginTransmission(0xD0>>1);
Wire.write(0x00); //Point to register 0x00
Wire.write(0x00); //Set Seconds
Wire.write(0x28); //Set Minutes
Wire.write(1<<6|1<<5|0x11); //Set 12hr bit and PM bit with Hours
Wire.endTransmission(true);
}

void loop()
{
if ((millis() - dispUpdateInterval) > 50)
{
dispUpdateInterval = millis();
Wire.beginTransmission(0xD0>>1);
Wire.write(0x00);
Wire.endTransmission(false);
Wire.requestFrom(0xD0>>1, 3);
timeS = Wire.read();
timeM = Wire.read();
timeH = Wire.read();
//Line 0
LCD.setCursor(0,0);
if (1<<5 & timeH) { lcdBufferCount = sprintf((char*)lcdBuffer[0], "%X:%02Xp", timeH & 0x1F, timeM, timeS); } //If the PM bit is set, format the display string with a 'p'
else { lcdBufferCount = sprintf((char*)lcdBuffer[0], "%X:%02Xa", timeH & 0x1F, timeM, timeS); } //If the PM bit is not set, format the display string with an 'a'
Wire.beginTransmission(0x50<<1);
Wire.write(lcdBuffer[0],16);
Wire.endTransmission(0x00);
}
}

 

You will also need my revision of the Newhaven Display LCD library.

 

Sprintf and the Arduino IDE

I’ve had to recompile the Arduino IDE to change the linking process in order to enable true “float” or “double” compatibility when using sprintf() or printf(). These functions are fantastically useful when looking to create a final string that can be sent to a display for example. Unfortunately the main Arduino IDE builds available link against a pared down version of the function that replaces floats with a nasty ‘?’. The recompiled build increases the final sketch build size for all sketches but it is worth it if you need float-related string parsing. For example, I am using the float-enabled sprintf function to place dynamic text (such as temperature or a raw value) in a fixed (predetermined) statement. Because my display is limited in character space, I need to manage display real-estate and can set fixed widths for things such as strings or temperatures etc.

If you wish to use my compiled version of the Arduino IDE, it can be downloaded here: http://josh.to/E2nS (OSX Only).

She’s Got a Pulse!

So I’ve been tinkering with some code to establish a true PWM signal from the Arduino in order to drive the boiler. Rather than implement a software based “Is it time yet?” approach, I thought I’d take advantage of the 16-bit Timer1 on the Atmega328P (the Pro Mini’s brain). It turns out, you can pre-scale the system clock that drives this timer to as low as about 30Hz, set the number of ticks that must occur before the timer resets, and finally set the number of ticks that must occur before the PWM pin is toggled. All of this requires accessing the system registers on the chip, but ultimately allows me to simply set a value and forget about. Moreover, this value can be updated every single program loop based on a simple PID calculation without glitching out any PWM cycles. All in all, it becomes an efficient and effective use of the underlying PWM hardware to manage a fairly passive process.

Here’s the code that I’m using in setup():


//Define Boiler PWM
DDRB |= 1<<PORTB1; //Sets Pin 9 As Output
TCCR1A = 0; //Clear Timer Counter Control Register A.
TCCR1B = 1<<WGM13; //Set WGM13 - Mode 8 Uses ICR1 as max and OCR1 as width. Mode is symmetrical!
ICR1 = 0x7FFF; //Defines the number of ticks (15 625Hz) per half PWM period (due to PWM symmetry).
OCR1A = 0x4000; //Match Value - Change this from 0x0000 to ICR1 value to adjust PWM.
TCCR1A |= 1<<COM1A1; //OC1A (Pin 9) Clear On Match Up, Set on Match Down.
TCCR1B |= 1<<CS12 | 1<<CS10; //Start Timer With 1024 Prescaler: 16MHz / 1024

All you have to do is change the value of register OCR1A and it will change the PWM value. Note that this value should never exceed the value stored in ICR1 otherwise the PWM will never toggle. ICR1 can be has high as 0xFFFF giving a period of ~8.1s.

This is now easily implemented in a PID control loop. My current algorithm will keep the temperature within 1°C. I’m working to refine it a bit more though :).

Hearing Noises – Silvia’s Craniotomy and the ADS1247

This specific update is going to be brief, but it’s to discuss the mild learning curve that’s required to implement sensitive devices in an electrically noisy environment. It turns out that both Silvia’s pump, as well as the three-way solenoid valve generate a significant amount of inductive noise when they’re switched on or off. So much so that it’s sufficient to overload the SPI bus between the Arduino and the ADS1247. The ADS1247 has inherent 50/60Hz filtering – but the SPI bus does not. With my initial testing, every time I energized the pump or solenoid valve, the ADS1247 would effectively go “dead” due to communication problems. It would require a complete power cycle of the setup to restore the system.

In my original design, I had the slave-select (SS) line of the ADS1247 tied permanently to ground as it’s the only device on the bus and will always serve as a slave. But it turns out, this pin can also be used pull the SPI bus into a high-impedance mode that effectively resets the SPI bus (but not the rest of the ADS1247) and effectively makes it “deaf” to this inductive noise. I tied this pin to the Arduino’s SS line (pin 10) and have now implemented toggling of the SS line in the code. According to the Atmega328P datasheet, the SS line can be safely used as a GPIO without forcing the Atmega328P into slave mode as long as it’s set to output and never to input. Note that the code accesses the registers directly. Since I’ve made this change, I’ve not had a single problem.

Note that the code accesses the registers directly. For anyone using this code, make sure to pull SS low when you want to communicate with the ADS1247 and high once you’re done. Out of good practice, you should also write a NOP (0xFF) to the device before pulling the SS high – this will ensure that the last few bits have been read in / out before the SS line goes high.


//A2D Setup
DDRB |= (1<<DDB5) | (1<<DDB3) | (1<<DDB2); //Set SCK, MOSI, SS Pins as Output
PORTB |= 1<<PORTB2; //Pull SS High to Disable Communications with ADS1247
SPCR = (1<<SPE) | (1<<MSTR) | (1<<SPR0) | (1<<CPHA); //Set SPI and Enable
PORTB &= ~(1<<PORTB2); //Pull SS Low to Enable Communications with ADS1247
SPI.transfer(0x06); //Reset
delay(210);
SPI.transfer(0x16); //SDATAC
SPI.transfer(0x4B); //Set IDAC1 Register (0Bh) Write 01h - Output reference current on ANIN0,1
SPI.transfer(0x00);
SPI.transfer(0x01);
SPI.transfer(0x4A); //Set IDAC0 Register (0Ah) Write 07h - Select 1.5mA reference current for RTD
SPI.transfer(0x00);
SPI.transfer(0x07);
SPI.transfer(0x43); //Set SYS0 Register (03h) Write 52h - PGA:32, Sample at 20sps
SPI.transfer(0x00);
SPI.transfer(0x52);
SPI.transfer(0x42); //Set MUX1 Register (02h) Write 30h - Select internal reference always on, internal ref connected to REF0 pins. Use 33h if wanting an on chip temp read.
SPI.transfer(0x00);
SPI.transfer(0x30);
SPI.transfer(0x40); //Set MUX0 Register (00h) Write 01h
SPI.transfer(0x00);
SPI.transfer(0x08);
SPI.transfer(0xFF); //NOP to prevent CS High too soon
PORTB |= 1<<PORTB2; //CS High

Measuring Temperature and Resistance on the Auber Instruments RTD using the ADS1247 and Arduino

Just wanted to see if I could get some real readings from the prototype design that I’ll use in the Arduino / Silvia Duetto Project. It’s alive!

I should say that I’m using the Callendar-Van Dusen equation and not a linear approximation to estimate temperature. The A2DResRatio value must be set and calculated as appropriate given the RREF, RBIAS and PGA setting used with the ADS1247.

For best results, view this in HD and with fullscreen!

Code:

#include <SPI.h>

//A2D Values
const double A2DConstA = 3.9083E-3;
const double A2DConstB = -5.775E-7;
const double A2DResRatio = 5.587936114E-6;
const double A2DResOffset = 150;

unsigned long A2DVal = 0x0;

void setup()
{
//A2D Setup
//Turn on SPI Bus
SPI.setClockDivider(SPI_CLOCK_DIV16);
SPI.setDataMode(0x04);
delay(2000);
SPI.begin();
SPI.transfer(0x06); //Reset
delay(3);
SPI.transfer(0x16); //SDATAC
SPI.transfer(0x4B); //Set IDAC1 Register (0Bh) Write 01h - Output reference current on ANIN0,1
SPI.transfer(0x00);
SPI.transfer(0x01);
SPI.transfer(0x4A); //Set IDAC0 Register (0Ah) Write 07h - Select 1.5mA reference current for RTD
SPI.transfer(0x00);
SPI.transfer(0x07);
SPI.transfer(0x43); //Set SYS0 Register (03h) Write 52h - PGA:32, Sample at 20sps
SPI.transfer(0x00);
SPI.transfer(0x52);
SPI.transfer(0x42); //Set MUX1 Register (02h) Write 30h - Select internal reference always on, internal ref connected to REF0 pins. Use 33h if wanting an on chip temp read.
SPI.transfer(0x00);
SPI.transfer(0x30);
SPI.transfer(0x40); //Set MUX0 Register (00h) Write 01h
SPI.transfer(0x00);
SPI.transfer(0x08);

//Enable Serial
Serial.begin(115200);
}

void loop()
{
//Reset A2D Storage Value
A2DVal = 0x0;

SPI.transfer(0x12); //Issue RDATA
A2DVal |= SPI.transfer(0xFF);
A2DVal <<= 8;
A2DVal |= SPI.transfer(0xFF);
A2DVal <<= 8;
A2DVal |= SPI.transfer(0xFF);

Serial.println(DtoT(A2DtoD(A2DVal)),2);

delay(1000);
}

double A2DtoD (long A2D)
{
if (A2D & 0x800000) {A2D |= ~0xFFFFFF;}
return A2D;
}

double DtoT(double A2D)
{
return -1 * ( A2DConstA / ( 2 * A2DConstB ) - sqrt( 25 * A2DConstA * A2DConstA + A2DConstB * ( A2D * A2DResRatio + A2DResOffset ) - 100 * A2DConstB ) / ( 10 * A2DConstB ) );
}

double DtoR(double A2D)
{
return A2D*A2DResRatio+A2DResOffset;
}

Time for an Espresso

So I’ve decided that I’d like the ability for the Arduino / Silvia Duetto to be able to maintain the time and consequently warm her belly up at specific times during the day. As any Silvia owner knows, it is extremely important to have both Silvia’s boiler as well as the group-head and portafilter up to temperature prior to pulling a shot.

I selected the Maxim / Dallas Semiconductors DS3232 as a suitable real-time clock (RTC). This handy little device has a built-in crystal (XTAL) oscillator and most importantly operates over an I2C bus. As pin space is limited on the Arduino Pro Mini, both the display I’ve selected and now this RTC have the ability to share the same two data pins. Moreover this RTC is rated for industrial temperatures at -40°C to 85°C and contains a built in temperature sensor to provide temperature compensation to the device (as the heat will fluctuate inside Silvia, so will the resonant frequency of the XTAL).

Maxim / Dallas Semiconductors also have a great sample program which means I got 4 of these chips for free! I’m off to wire up a test circuit. More to come soon!