Bathroom Mirror

bathroom led mirror _updated

[First, I’d like to apologize for my too long absence. I’m now a full time hardware engineer, since two years. My job and the projects I’m working on are exciting, but also quite demanding of my energy and time. My level of laziness being constant, I gave up on writing articles (even if I still do electronic projects at home). This new article is about something I built last year. That’s being said, let’s start:]

[TL;DR: scroll to the end of the article to see more photos, a video and github]

For these of you who have been following my blog, my biggest project so far is the USB temperature logger. I enjoyed making it, I like the outcome, but it has one problem: if you’re not an engineer or a bit technical, it’s not cool. I wanted to make something people would see and find cool (you can add vanity to my laziness). Something with light and not an other LED strip attached to an Arduino. And this time, I wanted an enclosure.

The inspiration came from movies and a poorly lit bathroom: a bathroom washbasin mirror, with lamps around it. To spice things up, it had to be at least dimmable.

Something like that, but round:

Hollywood-Mirror

After some experiments with lightbulbs and triacs, I decided it was a bad idea to have 220V-powered light bulbs (made of thin and sharp glass), ten centimeters from my head, when my face and hands are soaked with water … But I still wanted something looking like lightbulbs.

The solution came form Paulmann, a german manufacturer offering these things:

Paulmann-Bulb-Socket

It’s supposed to be a customizable system of light bulbs (they offer different sizes, shapes and colours for the “bulb” and you can screw halogen, fluo or LED sockets inside). A bit on the expensive side (10€ for a set) but I was only interested in the glass bulb anyway (the bulbs can be bought separately, for cheaper).

With these, I would be able to use LEDs solving many problems:

  • Safety
  • 60Hz whine
  • System complexity (no more 220V, triacs, isolation to handle, just PWM)

I also wanted the possibility to display time with the lights, so I decided to fit twelve of them, on a round shape. Here are my first sketches:

LED_Mirror_Sketch LED_Mirror_Sketch_2

Because the LEDs will be driven by a microcontroller, I could add some cool features:

  • A motion sensor (so the mirror lights up when one enters the bathroom)
  • A capacitive touch sensor dimmer
  • An internal clock, to adjust the the luminosity according to the time of the day or night.

Let’s make a list of early parts/requirements:

  • LEDs: x12 High power (~10W each) warm white
  • Microcontroller with 12 PWM, USB, RTCC
  • Capacitive sensor (slider)
  • Movement detector (PIR type, as small as possible)
  • Off the shelf power supply
  • The frame of the mirror should contain all the electronics
  • A mirror (duh!)

I started by finding the right dimensions and design for the enclosure/frame. With standard mirror sizes (ie. the ones available at my local hardware store), a size and spacing of the bulbs that would look harmonious, I got an inner frame diameter of 52cm (Ø50cm mirror) —20 inches. The frame is 8 cm (3,15″) wide and the bulbs have a diameter of 6 cm (2,36″).

It’s made of wood. All the electronics and LEDs are mounted on the bottom ring, the upper ring has holes for the bulbs and supports the sides of the frame. Some pictures of the construction:

LED mirror enclosure - rings

The face and base rings, with spacers glued to the face ring. (upside-down)

LED mirror enclosure - face ring - inner view  LED mirror enclosure - front ring

Front ring, with holes for the LEDs. The wooden pieces act like spacers between the front and back rings and support the 1mm thin side plywood (just bent and glued around).

LED mirror enclosure assembled

Completed, with pivoting support for the mirror.

LED mirror enclosure - completed - with bulbs

With the bulbs. They are glued with silicone joint from inside. The silicone gives some compliance, so the bulbs can move instead of immediately breaking at their base if hit.

At the same time, I worked on the LEDs and the driver:

I chose Bridgelux BXRA-40E0810-A-00 LEDs. Warm white and star shaped footprint. 980 lumens @28,1V 350mA.

BXRA-40E0810-A-00

For the controller, I found the TS19371 Boost white LED driver, with PWM input and OVP. From Taiwan Semiconductor. As it’s a one-off project, I didn’t care about long-term availability. The other components are quite standard.

TS19371CS

The LED current is calculated with the following equation:

Iled = 95mV / Rf

I have a 0,22 Ohm feedback resistor, resulting in a 430mA calculated LED current, which doesn’t match the values from the datasheet’s schematics or the current I measured.

Plot of the input power (@12V) and the LED power (@28V) vs. the PWM duty cycle:

TS19371 power vs PWM

Which gives the following efficiency (vs. PWM duty cycle):

TS19371 efficiency vs PWM

Not bad, especially the most comfortable luminosity is between 40 and 80%.

LED Driver Schematic (pdf)

LED-driver-PWM-Module-3D

There’s a bit of “whine” at some PWM values, probably due to the two big ceramic capacitors. (piezo effect). Later, I added big value capacitors (1000uF) across the power supply, every two driver boards.

Even if the LEDs are from a good brand and have a good efficacy, they get very hot without a heatsink. The problem is, the mirror frame is closed and doesn’t have any airflow inside. Putting a fan is of course, out of question, same with vent holes.

My solution was to mount the LEDs on aluminium rods, which act like a heat storage. I got twelve of these rods, 20m diameter and 25mm high, for one Euro each, already cut, on eBay:

BXRA LED Heatsinking

(first revision of the PCB)

Here’a graph of the temperature increase of a LED at the maximal voltage and current (100% PWM):

LED temperature curve

As you can see, no heatsinking isn’t an option. With the aluminium rods, the LEDs can be at 100% for quite long (bathroom timeframe). The software takes care of dimming the light after 15 minutes. There’s also a temperature sensor on one of the heatsinks. The maximal intensity is anyway too bright and shouldn’t happen too often. The startup luminosity is about 50%.

Some photos of the LEDs and drivers construction:

BXRA-40E-LEDS-heatsinks-2

Heatsinks with tapped holes (plus one mounting hole –M3– on the underside).

BXRA-40E-LEDS

PCB-to-pick-and-place

Driver PCBs with solder paste — ready to be populated.

PCBs-reflowed

After reflow.

LEDs-and-PCBs

Finished.

The next step was the capacitive touch dimmer.

I used the Atmel’s AT42QT2160 Qslide – Matrix Sensor IC. Quite powerful and versatile. It can have up to 16 keys or/and one slider of 8 keys/bits (0..255). For once, Atmel provides an exhaustive data sheet, plus a lot of design material for the keys/sliders/wheels. (Now, you can even get footprints for Altium designer).

It works by sending a charge pulse to one side of an electrode (the sensing element) and sampling it on the other side, comparing the changes over time. Fingers change the capacity of the electrode, as well as the intensity of the sensed electrical field. If the resulting signal goes below a threshold, the chip counts it as a touch. The electrodes can be laid out to form a linear pattern, making a slider or a wheel and the chip configured to output an X value corresponding to the touch position on the slider or wheel.

I followed Atmel’s recommendations for the design of my slider. I managed to get a very good definition, even through the thick wood. (On the first version of the mirror, the sensor was on the side, sensing touches through the 1mm thin plywood. After the update, it’s on the front and still works great through 5mm of plywood!).

Because of my requirements of sensitivity, wood thickness, length and definition, I designed a “Mutualy Coulped – One Dimensional – Two layer – Resistively Interpolated” Slider:

Large resistively interpolated capacitive slider

(Atmel’s “Touch Sensors Design Guide”)

The sampling signal is emitted on the Xn lines (top PCB side) sequentially and sampled on Y line (bottom side).

Which looks like that in real life:

AT42QT2160-Slider-PCB-Bottom

Bottom (fingers) side with the Y sampling traces.

AT42QT2160-Slider-PCB-Bottom

Top side: Xn electrodes. 0.8mm PCB, so it could be bent and follow the shape of the mirror frame. Even if the bend radius isn’t too small, I used 0402 components and tried to place them parallel to the bend. The PCB has a footprint for an EEPROM, sharing the sensor’s i2c bus. (The first microcontroller I used didn’t have any EEPROM).

Slider and AT42QT2160 Schematics (pdf)

The data sheet gives two important measures to make:

AT42QT2160-Cs-X7 Sampling Cap

The sampling capacitor Cs voltage. It shouldn’t exceed -0,25V and the ramp should be linear.

AT42QT2160-Charge-Transfert-Pulse

The charge transfer pulse (measured with a coin on the sensing surface): Should be square.

The AT42QT2160 connects to my microcontroller via an i2c interface. It has a lot of registers to tweak configuration and parameters for the touch detection. The sensitivity can be adjusted without having to change components on the PCB.

A “change” output pin can be used as an interrupt and relieves the micro controller from sending continuous interrogations on the i2c bus.

Led mirror AT42QT2160 touch sensor PCB on the frame

The touch sensor attached to the frame with double sided tape (on the front of the frame, after the update). Note the silicone joint holding the bulbs and the wooden spacers supporting the sides of the frame.

The motion sensor.

PIR sensors can be tricky to implement, so I took the easy way and used the Zmotion Detection Module II from Zilog. It costs 10€ from Digikey and only needs a 3.3V power supply and an RS232 interface.

I won’t write a lot about it, first because its implementation is straight forward and second, because I don’t use it anymore after the update, so I didn’t write the code to support it in the new microcontroller.

ZEPIR0BAS02MODG PIR sensor Zilog

It features a small microcontroller and a pre calibrated PIR element. Plug and Play.

It also has parameters to tweak the sensibility of the detection and can even tell the direction of the detected movement (going from left to right or right to left).

An other reason why I chose this sensor board, is because the PIR lens is really tiny and once mounted inside the mirror, it’s almost invisible (because of the shape of the bathroom, the sensor was on the side, even more difficult to see).

The light coming from the LEDs was messing with the sensor and it detected a continuous movement once the mirror was on. I had to isolate the sensor board with aluminium foil and tape. Not a big deal.

Putting everything together and giving it a brain.

Ok, now we have our mirror and the frame, 12 LEDs, the touch dimmer and the movement sensor. How do we make everything work?

For the first version, I used a PIC18F87J50. An Xmega256A3BU after the update. I won’t start a debate on Microchip vs. Atmel (that should be a future article), especially the mirror worked exactly the same with both of the microcontrollers.

PWM

If you’re familiar with these two microcontrollers, you’ll know they don’t have 12 PWM outputs.

So how can they control the intensity of each of the LEDs?

Simple: Software PWM. Because the LED driver has its own switching frequency (1,2MHz) and an enable input that can be used as PWM dimming, the PWM frequency coming from the micro controller doesn’t need to be too high. In my case, it’s between 120 and 300Hz. Enough to have a good persistence of vision.

(actually, the software of the first prototype was a bit slower and did a cool stroboscopic effect, you could see the water drops going up when you were taking a shower. Psychedelic, but not that funny at 7am, half asleep).

To implement my software PWM, I’m using a timer which fires an interrupt and increases a 8 bit “PWM counter” variable. Each LED has a variable with the PWM value it’s set to. In the same interrupt, I compare each of the LED variables to the PWM counter, setting the corresponding LED pin high, if the LED PWM value is higher than the PWM counter, setting it low if the LED PWM value is smaller than the counter.

There is a few more subtle things:

I’m using the Xmega virtual ports feature, it speeds up the execution of the interrupt (one ASM instruction instead of three or four).

Each LED value is compared sequentially, so they don’t switch on or off at exactly the same time. You cal call it a sort of crude spread-spectrum PWM 🙂

Schematics for my ATxmega256A3BU module

I’m using a 16MHz crystal to clock the Xmega, because the internal RC oscillator had too much jitter (which isn’t a big deal here, it just annoyed me).

Here’s the interrupt jitter with the internal RC oscillator:

Xmega Internal RC oscillator jitter

And (almost) the same measure after switching the oscillator to an external crystal:

Xmega External Crystal oscillator jitter

3,5us vs. 10ns Not bad.

I’m also using the USB interface to be able to monitor the microcontroller and set a few parameters (date/time, touch sensor sensibility, read the temperature etc.):

ATXmega256A3BU USB console

Measuring Temperature

I’m a big fan of the TCN75 i2c (or similar) temperature sensors, but here, I wanted something I could directly attach to one of the LEDs heatsink:

NTCALUG03A473H

It’s a Thermistor ( NTCALUG03A473H ), a temperature-dependant resistor. I hooked it up to the Xmega’s A/D converter.

My voltage to temperature conversion is very crude, an array of voltages every 5°C, plus an interpolation to get 3°C resolution. I didn’t need more, because I’m just using it to dim the LEDs when the heatsink temperature is too high.

Led-mirror-NTCALUG03A473H

The thermistor, screwed to the side of the upper LED heatsink.

An other feature I found cool to implement, is an anti-fog system. To avoid condensation on the mirror when you’re taking a shower, the mirror is heated up with a resistive wire attached with kapton tape on the back side of the mirror.

Resistive Wire

datasheet

The one I chose has a 6,93 Ohms resistance per meter. It gives a current of 1,73A per meter with a 12V power supply.

It’s annoying to work with, because it’s very springy. And it’s even more annoying to solder, even if the data sheet states the opposite.

Unfortunately, the way I attached the mirror to the frame when I built it one year ago prevented me from taping the wire till the center, so only the outer part of the mirror is heated. It works nevertheless not too bad to keep the center free of condensation.

I have two loops of this wire in parallel, taking 2A in total and heating up to 50-60°C. The kapton tape presses the wire against the mirror, ensuring a good enough thermal transfer.

The heating is switched on and off by the microcontroller via a MOSFET transistor (2KS4017).

Mirror-Heating-resistive-wire

Resistive wire kaptoned to the mirror’s back.

The power supply.

I said I didn’t want to play with AC and the whole mirror was running on 12Vdc. So what powers it?

PWS 150 150W power supply

A 150W 12V AC-DC converter. 150W is more than enough to power the 12 LEDs and the 2A resistive wire.

In the first version of the mirror (and the first bathroom), it was sitting on the side and the mirror was switched on and off by the motion sensor.

After I moved to my new place, it’s actually connected to the bathroom’s socket for the washbasin light and the mirror is activated with the switch on the wall (it’s a double switch, one for the ceiling lamp, the other for the mirror).

I put some white fabric sleeve on the wires, so they don’t look too bad.

Putting things together.

LED Mirror bottom

The twelve LEDs are attached round the enclosure’s baseplate with a screw, from the back of the baseplate to a taped hole in the bottom of each heatsink. The driver boards are held with a wood screw, directly on the baseplate.

LED Mirror Bottom wired

The driver boards’ power supply is just wired in parallel (the thick blue and brown wires). Each driver’s PWM input is connected to the microcontroller board.

LED bathroom mirror First test

First power up. I taped the bulbs to the heatsinks, because the LEDs are really powerful and hurt eyes if looked directly at them.

The development “simulator”

Every time I wanted to develop new functions for the firmware, I had to take the mirror from the bathroom, put it on my bench, open the enclosure, connect the AVR JTAG debugger, program the firmware, test it and put the mirror back in the bathroom. Not very convenient.

Because I made several ATxmega256A3BU boards and I order my PCBs from iTead Studio, so I always get ten of them, I could replicate the mirror on my bench, with 3mm LEDs instead of the Bridgelux ones. It has all the functionalities of the mirror, so I can take as much time as I want to write the firmware and to test it:

Led-Mirror-Simulator

Led-Mirror-Simulator-AT42QT2160-ATxmega256A3BU

When I’m sure the new firmware is ok, I just update the real mirror via the USB bootloader. (I made a cutout on the top of the enclosure for the USB plug, next to the 12V DC jack socket).

Photos of the first version/bathroom:

Bathroom LED mirror

Bathroom lightbulb mirror

(The PIR motion sensor lens is clearly visible on the side of the frame)

When I moved to a new flat, after the update:

bathroom led mirror _updated

And a quick video showing the main features:

The firmware sources can be downloaded from my github:

https://github.com/Pickandplace/LEDmirror

Conclusion:

The first version of the mirror has been running for one year without any problem. The updated version, for six months now. I wish I had written a series of articles along the design and build of this project, because even if now I still remember all of my process and decisions, the resulting article is a bit long and not detailed enough.

Concerning the mirror, having this big wooden piece the mirror is glued to, prevented me from putting the heating on all of its surface. It works ok, but sometimes it fails to keep the center of the mirror fog free.

If I have time, I’ll try to find a way to put resistive wire on the middle too.

Because the 150W AC/DC power supply has a lot of capacitance and the 12V rail has big capacitors, there’s a one or two seconds lag between the moment you switch the mirror off and the LEDs going actually off. It would be a nice to implement a 220V detection circuit, so the microcontroller knows when the AC current disappears and can switch the LEDs off quicker (and also with an “animation”).

Designing a simple and cheap temperature logger. Part 7: Schematics, PCB and source code.

I decided to publish all this project under Creative Commons license (Attribution, Derivatives, Non commercial, ShareAlike). The source code contains a FAT12 filesystem that can be reused to make custom flash drives for other projects.

USB Temperature Logger

(PCB version 1.2, hence the misaligned 3.3V regulator to include a diode. Corrected in the V2.0)

USB Temperature Logger 2

USB Temp Logger Schematics

You’ll find the schematics here: Simple USB temperature logger schematics

The Altium files (including a routed PCB) there: Simple USB temperature logger Altium files

The source code (based on Microchip Applications libraries -Device – Mass Storage – SD Card data logger- MPLABX): Simple USB temperature logger source code

The source code could be improved to increase the robustness of the logger (especially in the case of battery power failure while on logging mode). The PCB allows the monitoring of the battery voltage, it just needs to be implemented in the firmware.

I hope my work will be useful to someone!

Don’t hesitate to contact me (mail in the About section) for questions or comments!

Designing a simple and cheap temperature logger. Part 4: The junk in the trunk.

I somehow managed to solve the problem of the serial flash memory write block size vs. the erase block size. I just had to write a custom FAT12 filesystem..

As seen before, the Temperature Logger will act like an USB memory stick. There will be at least two files. One with the time stamped temperature measurements and one file allowing the user to set the logger’s configuration.

The configuration file must be accessible by any OS and modifiable. It will configure the temperature measurement period, from one every second till one every 24h (86400 seconds), in seconds. It’s also used to synchronize the PIC’s real time clock and calendar to the computer’s current time and date.

So far, it looks like that:

#THIS IS THE CONFIGURATION FILE
Setting the temperature logging period, in seconds
1<period<86400
Don't forget to add a "#" before the number:
#70
#END OF THE CONFIGURATION FILE

Pretty simple. When it’s saved, the PIC will read the number following the “#” and the time and date of the file modification.

The FAT filesystem is divided into five sections, each of them divided into sectors:

  • The Master Boot Record
  • The Boot Record
  • The FAT (doubled)
  • The Directory
  • The Data

When a file is written, its content will be placed in the Data section. The name and other information will be in the Directory. The FAT will then describe where the different data pieces of the file are located on the disk.

I took the standard sector size of 512 bytes (the size of the PIC’s buffer and the double of the EEPROM’s write block).

The MBR, the Boot Sector and the Directory are 512B from the OS point of view and 4KB on the EEPROM. I need five FAT pages to map the capacity of my EEPROM. Here’s a map of my filesystem:

FAT12 MicrochipAll the sectors occupied by the filesystem sections are 4K wide.

(The help.txt file contains the basic explanations on how to start/stop temperature acquisition and how to erase the data.csv file)

I modified the Microchip’s SD-SPI.c file to be able to talk to my memory, instead of a SD card. The main functions are:

BYTE MDD_SDSPI_SectorRead(DWORD sector_addr, BYTE* buffer)
BYTE MDD_SDSPI_SectorWrite(DWORD sector_addr, BYTE* buffer, BYTE allowWriteToZero)

They both take the address of the EEPROM sector to be written to, in LBA format (ie. the sector numbers) and translate them to the real EEPROM address. The buffer is the 512B PIC’s memory zone to write to the EEPROM or to fill with the EEPROM data.

An if statement chooses the address shift. Everything that’s below the DATA.CSV file will be 12-bits shifted ($02 becomes $2000) and the 4KB zone will be erased before any write occurs. If the write or read is in the zone where the temperature measurements are, the shift will be relative to the DATA.CSV address ($2E000), and looks like:

Address = Address - $2E
Address = Address << 9
Address = Address + $2E000

Which means, for example, that if we read or write the third sector of DATA.CSV, the function will be called with $30 and will result in the EEPROM address of $2E400, which is the third sector of the EEPROM after $2E000.

Of course, any write to this zone by the OS has to be forbidden.

For this, the Microchip USB Mass Storage Device driver uses the same functions as above, but in the usb_function_msd.c file. It uses an other buffer, msd_buffer, 512B too.

Every time the computer OS wants to read or write into the EEPROM, the data is cached in this buffer. Two functions are doing that:

BYTE MSDReadHandler(void)

BYTE MSDWriteHandler(void)

In each of these functions, I inserted a test to know what and where the OS wants to read or write.

In the case of a read, I’m not doing a lot for now (the MDD_SDSPI_SectorRead function already handles it).

For the writes, I first test if the OS is writing the data of config.txt file. If yes, I search for the period string and write the data to the memory. If the OS writes the File Dierctory, I read out the new date of the config.txt file but I write the original contents of the File Directory in the EEPROM instead of the new ones.

For any other write from the OS, I just answer the write was OK, but don’t write the actual data to the EEPROM.

This way, I’m sure the OS won’t fill the memory with anything else but the right data. It’s especially true for OSX, who wants to write Spotlight, Trash and info files in any USB stick it finds.

Here’s a simplified diagram of the modified part of SectorWrite function:

  • if msd_buffer contains the config.txt start and end string -> Extract the period, write the buffer to the config.txt file address.
  • if msd_buffer contains the File Directory -> Extract the time and date from config.txt, if it’s newer than the original date. Erase the msd_buffer and fill it with the original contents, minus the date and time fields. Write the buffer to the File Directory address.
  • in any other case, perform a dummy write.

That way, we save a lot of space in the EEPROM. If it would have been divided into 4KB sectors, with only the first 512B usable, we would have ended with a ≈100KB disk ((($FFFFF-$2E000)/$1000)*$200). Now, even if some space is wasted for the filesystem, we have a ≈840KB drive ($FFFFF-$2E000). It means something like 30 000 timestamped temperature measurements.

Some precisions about the FAT:

The FAT is used to keep track of every file on the disk. Each of the FAT entries matches a sector on the disk (that’s why my FAT is 5 sectors long, we need 5*(512/1,5) bytes to map our 1648 sectors). Here’s my FAT12 capture, as it comes after initialization:

FAT - FAT12 - Temperature Logger - FormatedThe first two entries are sector 0 and sector 1. They’re reserved. That’s why the data zone is located starting $2C000 (each sector is $1000 bytes before the DATA.CSV file and the FAT ends at $0C000).

The three next entries are $FFF,$FFF and $FFF which means that the sector contains one complete file (config.txt, help.txt and data.csv). (It’s easy to understand why small files are wasting space: Every file has to occupy at least one sector, even if it’s smaller than the size of the sector)

When a file is spread on more than one sector, its FAT entry will have the next sector address.

Unfortunately, the entries are coded in 12 bits values, and in big-endian format. If we want to write xyz for the sector 4 and XYZ for the sector 5, we have to write:

Yes, could be easier (it actually is, with FAT16 and FAT32, but our memory is too small to use those formats)

Here’s an example with the data.csv file spreading on 15 sectors (512*15 Bytes wide):

FAT12 - 19 sectors

If we decode the 12-bit addresses, it gives:

FAT12 - Decoded

There is one file in sector 2. One in sector 3. The third file starts on sector 4 and goes till sector 18.

I made a function to add entries every time a buffer full of temperature logs is written into the EEPROM. The 12-bit address management looks like that:

if(FatSector & 1) //Odd Sector Number
 {
 FATEntryPtr = FatSector-1;
 FATEntryPtr = FATEntryPtr + (FATEntryPtr>>1); // *1,5
 FatDWordA.w[0] = ( (FatSector+1) & 0xFFF);
 FatDWordA.w[1] = ( (FatSector) & 0xFFF);
 //We have:  0x yz 0X YZ and we want:
 //          00 yz Zx XY
 FatDWordB.v[0] = ((FatDWordA.v[1]&0x0F)<<4) | ((FatDWordA.v[0]&0xF0)>>4);
 FatDWordB.v[1] = ((FatDWordA.v[3]&0x0F)) | ((FatDWordA.v[0]&0x0F)<<4);
 FatDWordB.v[2] = FatDWordA.v[2];
 //The FatDWordB Contains what we want to write to the FAT, for the current //Sector and the next one.
 msd_buffer[FATEntryPtr] = FatDWordB.v[2];
 msd_buffer[FATEntryPtr+1] = FatDWordB.v[1];
 msd_buffer[FATEntryPtr+2] = FatDWordB.v[0];
 msd_buffer[FATEntryPtr+3] = 0xFF; //And write the end of the file for the //next sector entry
 msd_buffer[FATEntryPtr+4] = 0x0F;
 }
else //Even Number
 {
 FATEntryPtr = FatSector;
 FATEntryPtr = FATEntryPtr + (FATEntryPtr>>1); // *1,5
 //We want to write the next sector address in the current one, plus the //end of the file on the next sector
 FatDWordA.w[0] = 0x0FFF;    //End of the file for the next sector
 FatDWordA.w[1] = ( (FatSector+1) & 0xFFF); //In the current sector: the next //sector address.
 //We have:  0x yz 0X YZ and we want:
 //          00 yz Zx XY
 FatDWordB.v[0] = ((FatDWordA.v[1]&0x0F)<<4) | ((FatDWordA.v[0]&0xF0)>>4);
 FatDWordB.v[1] = ((FatDWordA.v[3]&0x0F)) | ((FatDWordA.v[0]&0x0F)<<4);
 FatDWordB.v[2] = FatDWordA.v[2];
 //The FatDWordB Contains what we want to write to the FAT, for the current //Sector and the next one.
 msd_buffer[FATEntryPtr] = FatDWordB.v[2];
 msd_buffer[FATEntryPtr+1] = FatDWordB.v[1];
 msd_buffer[FATEntryPtr+2] = FatDWordB.v[0];
 }

I’m using the msd_buffer to save the FAT during temperature acquisition (and the gDataBuffer to save the temperature logs) between two writes to the EEPROM, when on battery power.

Every time a gDataBuffer is full, it’s written to the EEPROM in the data zone and a new FAT entry is added to the msd_buffer. Once this one is full, it’s written to the EEPROM at the FAT address.

The variable FatSector contains the current sector in the FAT. FatDWordA and FatDWordB are temporary Double-Word variables used to compute the three-bytes-12bits FAT entries.

This is MCC18 PIC18 code, based on Microchip USB and MSD libraries. I wanted to share this snippet, as I couldn’t find any good PIC code example on the internet. I’m sure it could be optimized and simplified, but like that, it’s maybe easier to understand how it works.

I think it’s all for now. My next step will be to consolidate my program and code the power-managed temperature acquisition. I already have the Temp. sensor, EEPROM, USB and RTCC working.

I also have to make a new PCB for the logger, something more USB-key shaped, with options for a rechargable battery. It should be my next blog post.

Stay tuned and as always, don’t hesitate to report any error.

Designing a simple and cheap temperature logger. Part 3: Open collector, FATs and de-debugging.

I had at last some time to work on the project. The prototype PCB is (at least, electrically) functional.

I started with making the TMP102 temperature sensor work. Nothing to say in particular, it’s just an other I2C-small-package-sensor. Or rather SMBus, but apart from the minimum speed clock requirements on the SMBus, the two protocols are alike.

On boot-up, the sensor gets configured and put to shutdown mode. Then, I use the “One Shot conversion” feature. When the right command is sent, the sensor starts a one-time temperature acquisition and returns to shutdown state, allowing to save as much as energy as possible.

The precision of the sensor is typically 0.5°C over its standard temperature range (-25°C to +85°C, the one I’ll be using) and the resolution is 0,0625°C (the LSB).

The temperature results are read on two bytes (12bits resolution). The first byte is the number before the comma, 2’s complemented for negative temperatures. The second byte contains the number after the comma. It has to be multiplied by 0.0625°C to get the real number.

I thought that a two digits after the comma precision should be enough (ie. by rounding), and instead of wasting CPU time with multiplication, I just made a string array containing all the results:

Temp_Precision[34]=”00061319253138445056636975818894″

Then, when the second temperature byte is read (Temp1byte), I just have to index the array with it (after a 4-bit shift, because it comes left-aligned). “TempASCII” is a string containing the temperature value:

TempASCII[i] = ',';
TempASCII[i+1] = Temp_Precision[Temp1byte*2];
TempASCII[i+2] = Temp_Precision[Temp1byte*2+1];

Which gives the following numbers vs. the original 0,0625-stepped ones, in °C:

The array could of course contain all the non-rounded numbers:

Temp_Precision[64]="0000062512501875250031253750437550005625625068757500812587509375"
TempASCII[i] = ',';
TempASCII[i+1] = Temp_Precision[Temp1byte*4];
TempASCII[i+2] = Temp_Precision[Temp1byte*4+1];
TempASCII[i+3] = Temp_Precision[Temp1byte*4+2];
TempASCII[i+4] = Temp_Precision[Temp1byte*4+3];

And so on. Useful trick instead of making a 0,0625 multiplication.

As said in earlier posts, I want to store the temperature readings in an Atmel AT25DF081 EEPROM. I chose it because it has a big capacity (8Mb) and a really low voltage supply value (1,8V). It connects via SPI to the microcontroller.

Now, with the great help of Microchip and its PICs I/Os being configurated as “Analog inputs” on startup and read as “0” when one forgets to set the right registers (ADCON0 & ADCON1), I had the great chance to spend two days measuring the EEPROM SPI link. Who would know the Analog ports “ANx” were also on the PORTB, and not only on the PORTA with ‘A’, as anolog? I know, RTFM.

During my investigations of the now perfectly working SPI, I could notice that the serial output pin of the memory (SDO) wasn’t an open collector, as I thought. It’s a pity it’s written nowhere in the Atmel’s datasheet. The Maximum input voltage of “3,8V”, Voh min of “Vcc-0,2” and no value for Voh max leave some room for imagination.

Well, the PIC seems to recognize the 1,8V SDO high state as high, even at higher speed, so it’s not such a big concern. A diode between the pin and the PIC will increase the EEPROM Voh to approx 2,5V (even if it also increases the EEPROM Vol of 0,6V). It could be an optional component on the final PCB design, replaced by a 0 Ohm resistor if the tests show no logical state decoding problem.

On the software page:

I took an example app from the Microchip’s USB libraries called “USB SD Card data logger”. It’s supposed to be an application for a logger with a SD card, being able to write on the card a value from a potentiometer.

It’s using the files SD-SPI.c and .h to access the SD Card, part of the Microchip’s MDD Filesystem. I started with these files and re-wrote them to make the project work with my EEPROM. Of course, that’s where the problems started..

On the filesystem, the default sector size is 512 bytes. As the PIC has to buffer the sector it’s writing to or reading from, there must be the same amount of memory allocated to the buffer as the size of the sector.  With the PIC18F26J50 I’m using, it cannot be bigger as 512 bytes.

The AT25DF081 EEPROM is organized into 256 bytes program pages. As they are smaller than the sector, it’s not a problem (I’m just making an address shift when the PIC wants to write more than 256 bytes). Reading is even easier, as there’s no page limit (it can be read starting form 0x000000 till 0x0FFFFF, the address counter on the EEPROM increments itself).

It gets complicated when you notice that the EEPROM’s erasing granularity doesn’t match the writing one. The smallest block to be erased is 4KB (then, 32KB, 64KB and 1MB) which means that to change even only one bit, all the 4KB must be read, erased and re-programmed.

AT25DF081 Memory space diagram

For now, I’m using only the first 512 Bytes of each 4KB page, but it limits the available space to 256 sectors of 512 bytes. That’s 128KB. Minus the space required for the filesystem and the sector for the logger configuration file, that leaves roughly 120KB of storage for the temperature measurements. Each of them will be something like “12/12/2011;23:59:59;127,00←↓” which is 28 Bytes. Divide 120KB by 28B, you get more or less a 40 000 temperature-acquisitions-worth-logger. Could be better and the idea of wasting 90% of my EEPROM is bad.

So, what exactly do we have to store in the EEPROM?

  • The FAT12 or 16 filesystem (3 or 4 sectors)
  • The configuration file (1 or 2 sector)
  • The temperature data

The FAT and the configuration file must be accessed often and must be user, computer-OS and PIC modifiable. But, the temperature data will only be written directly by the PIC and then only read by the user when the logging is over. The only modifications they (or rather the file containing them) will be subject to, are adding new measurements or erasing all the data for a new logging round. All that done by the PIC, which has direct access the the EEPROM.

I’ll need to modify the Microchip’s MMD filesystem routines to be able to directly write into a reserved block for the temp. measurements on the EEPROM. Plus, make the appropriate modifications in the FAT to allow a computer OS to read them, without being able to modify anything on the reserved block.

Quite a piece of work, especially because the library linking the USB procedures to the disk is complicated. And with optimized C code, it’s quite hard to debug the program, as the code pointer is jumping around all the time (I even thought it was a problem with my debugger/compiler/program/stack)

That’s for the next episode. I’ll also try to post the two files (SD-SPI.c and .h) I modified to use the AT25DF081 with the Microchip’s MDD and I’ll say a few words about the ICD3 debugger.