A few website issues but everything is OK now.

Sorry to all of you who have been having problems with the site this past week. Someone managed to get some malicious code into the site either via a PHP exploit or sniffing my FTP password.

I have managed to get everything cleaned up, reviewed my security practices (no more FTP) and updated my WordPress installation. If you find any images are missing or something on the site isn’t as it was then please let me know.

Apologies to anyone how has been infected by malware. The site was apparently spreading malware to unprotected machines between the dates 19th and 27th of October.

Butterfly Logger Firmware 0.30

In an unprecedented move I’ve released another version of the firmware for the AVR Butterfly Logger within a few months of a previous release. This was a minor change just adding MAX6675 based J and K thermocouple support.

You can download the source code from sourceforge, with the changes from the previous version are listed below.

Changes summary:
- MAX6675 K-Type Thermocouple logging (adjustable averaging)
- MAX6675 available on 'T' via the USART.
- MAX6675 available on LCD via 'THERMOCOUPLE' Menu. 

The new library has configurable support for averaging readings but each individual reading takes 200ms so an 8 point average will take 1.6s to complete. If you use this averaging feature please make sure that your logging interval is long enough to ensure enough time for the complete reading to finish. While thermocouples are capable of measuring wide ranges of temperature both positive and negative the MAX6675 is limited to returning readings between 0°C and +1024°C. If the chip detects an open thermocouple then the system will log ‘-1’ as the temperature.

The MAX6675 connects to the AVR Butterfly via the SPI bus. This bus is also used by the onboard Dataflash and the Kionix acceleration sensors (when in use). As this bus is already available the only additional pins required is a single chip select (CS). The library currently only supports a single thermocouple input but the code could easily be expanded to suport multiple MAX6675s requiring a single CS line for each additional chip and thermocouple.

The SPI bus is available on PORTB and also via the ISP connector. An example breakout PCB for the MAX6675 was published here. Default wiring from the this PCB to the AVR Butterfly Logger is given below:

PCB MAX6675 Butterfly J403 (ISP) J405 (USI)
1 1 GND GND 6 4
2 7 MISO PB3 1
3 6 CS PE6 3
4 5 SCK PB1 3
5 4 VCC VCC 2

More information about the data logger project can be found at the project website.

Any questions can be asked in the project forums.

Butterfly Logger Firmware 0.29

I’ve finally managed to get another release of the Butterfly Logger firmware done. I was supposed to have released a lot of this a couple of years ago but it got lost in the ether somewhere.

Without any further ado the source code is here, with the changes from the previous version are listed below.

Changes summary:
- Added  'j' and 'J' commands to read SHT75 Humidity and Temp.
- Fixed the SHT75 clock waveform (now it looks  symetrical )
- Added ENABLE_SHT_CALCS option to print/store real world values for SHT75
- Tuned vref for more accurate results. (Battery voltage and dependant SHT75 calculations)
- Added Kionix accelerometer logging
- Fixed vref ( done by matthias.weisser )
- Make file uses no-inline-small-functions ( done by matthias.weisser )
- Direction ADCs are now refered to as Auxilliary ADCs

More information about the data logger project can be found at the project website.

Any questions can be asked in the project forums, here.

Using thermocouples with the Reprap Mendel

Using thermocouples with the Reprap Mendel is pretty straight forward, although I did overcome a couple of issues in getting it working.

A known issue with the MAX6675 Arduino/Reprap code

The MAX6675 chip can take up to 220ms to take a reading from the thermocouple and the reading begins automatically after the previous read. If another read is attempted before the current reading has been completed then the chip returns the previous value and restartsthe internal analog to digital the conversion. This means that if you read from the chip every 150ms or so you will always get the same first reading returned again and again.

The arduino library code needs a 220ms delay between multiple reads to allow for the MAX6675 chip to take another reading. In the current state the library simply reads the same value repeatedly. Luckily it is a known issue documented on the project’s site and is not hard to fix.

The taking of a single reading rather than taking a number of readings and averaging will contribute to a high noise level in the thermocouple readings when using this software.

Discovering the problem

When testing the PCB with the library code everything works fine. The arduino returns a reading every second and everything looks good.

When using the Reprap firmware with the thermocouple code it always returned the first value read from the chip (usually 24°C or so). This lead to things getting pretty hot as the extruder would turn the heater to full duty cycle while recording no change in temperature.

I first looked at the SPI lines on the scope to see if there was excessive noise or some other issue. I also compared this side by side with the ‘working’ arduino system. The scope traces are shown below. The reference channels (R1 and R2) are the reprap firmware and the bottom traces (1 and 2) are the arduino. I noticed nothing wrong with the traces, they both looked very good.

Reprap code compared to aduino library code communicating to the MAX6675.

It was here however that I noticed that the arduino code calls another read to the chip immediately after the preceding read. After a bit more investigation I noticed that the library software calls the temperature reading function 5 times consecutively and always returns 5 identical results. A quick read of the data sheet confirmed the typical conversion time to be 170ms with a maximum of 220ms. The arduino library starts a conversion just 20μs after finishing reading the last. Reading the data sheet further also confirmed the action of restarting a conversion every time the chip is read.

I monitored the lines on a scope and found that the Reprap software was indeed reading the chip every 136ms. To fix this I simply changed SLOW_CLOCK in configuration.h to 8089 from 5000. Based on the current delay giving 136ms, this gives 220ms delay between calls to the thermocouple chip. After making this change to the firmware the thermocouple now works perfectly.

Time between calls to the MAX6675 thermocouple chip using the standard firmware on my reprap.

Fixing the problem

A more robust fix to the reprap extruder firmware is shown below, but I have not tested this code yet. This simply exits the temperature reading method if enough time has not elapsed between calls to the function. The advantage of this is that the main loop can run as fast as it needs to without impacting on the operation of the thermocouple sensors.

#define MAX6675_CONVERSION_TIME 220 // number of milliseconds between valid temperature readings

  int value = 0;
  byte error_tc;
  static unsigned long last_read_time=0; // number of milliseconds since power on at last MAX6675 read 
  unsigned long time_since_last_read=0;  // number of milliseconds since last read of MAX6675 

  /* check the chip is ready to produce a new sample */

  /* only read from the chip if a conversion has been completed */
  if(time_since_last_read > MAX6675_CONVERSION_TIME){
    digitalWrite(TC_0, 0); // Enable device
    /* Cycle the clock for dummy bit 15 */
    /* Read bits 14-3 from MAX6675 for the Temp
     	 Loop for each bit reading the value 
    for (int i=11; i>=0; i--)
      digitalWrite(SCK,1);  // Set Clock to HIGH
      value += digitalRead(SO) << i;  // Read data and add it to our variable
      digitalWrite(SCK,0);  // Set Clock to LOW
    /* Read the TC Input inp to check for TC Errors */
    digitalWrite(SCK,1); // Set Clock to HIGH
    error_tc = digitalRead(SO); // Read data
    digitalWrite(SCK,0);  // Set Clock to LOW
    digitalWrite(TC_0, 1); //Disable Device
    last_read_time = millis(); //remember the read time for next time
      currentTemperature = 2000;
      currentTemperature = value>>2;


The arduino library can be improved by simply inserting the following at line 55 of the file MAX6675.cpp just before the end of the for loop.

if (i>1) delay(220); // Wait 220ms for next sample to be ready

This will force a 220ms delay when reading the thermocouple multiple times via the read_temp(int samples) function. Alternatively you could use the millis() function to track the last time it was read and automatically delay the read by the appropriate amount.

Ponoko now fabricating in the UK via RazorLAB

You’ve probably all noticed by now that Ponoko are now offering fabrication facilities in the UK via RazorLAB. I thought I’d post to show off my new laser etched bed for my Reprap Mendel. I could have cut this one by hand, but I got it lasercut and etched for reasons of curiosity.

An MDF bed for my Reprap Mendel. Laser etched and cut at RazorLAB.

I have to say that the service and turnaround from Ponoko and RazorLAB was great. Ponoko only promises a 28 day turnaround for non Prime members. My order was shipped in 3 days! Fantastic! The bed looks great and I’m happy with the results. For reference, the material used is 3.6mm ‘Natural MDF’ , and the text and logo are done in ‘Medium Raster’. All files were prepared using Inkscape.

Although I think the bed is a little on the thin side for a perfect print surface, it is a lot flatter than the curved 4.8mm aluminium bed I have been using up until now.

Calculating E_STEPS_PER_MM for the Reprap Mendel

UPDATE: Nophead now covers this better in this post.

In the course of building my extruder I chose not to modify my stepper motor. Instead of having a splined shaft on the motor I decided to use a splined pinch wheel similar to the brass M4 insert used in the geared extruder driver. My pinch wheel is shown below in Figure 1. By making some simple assumptions and using basic geometry I was able to calculate the necessary firmware parameter to accommodate my changes from the original design.

Figure 1: Splined pinch wheel. It is designed to fit a shaft with a diameter of 5mm and is fixed in place using a M3x3 grub screw.

I wanted to keep the motor unmodified so that it would be interchangeable with any of the other motors I’ve used on the reprap. The upside of this is that I don’t need to keep a separate modified motor as a spare just for the extruder. The downside of this is that the default value for E_STEPS_PER_MM used in the reprap motherboard firmware would not be correct for my extruder.

What does E_STEPS_PER_MM do?

E_STEPS_PER_MM is defined in the firmware documentation as “the number of steps that the extruder stepper needs to take to produce one millimetre of filament.” The firmware then uses this value to calculate the number of steps needed to drive the extruder in relation the print head’s current horizontal movement. If this value is wrong then the reprap will push out too much or too little extrusion into the print.

In the absence of any commissioning information in the official wiki for the reprap, once construction of the reprap was complete, I was left to adjust the value empirically.

As I had no way of knowing just how different my untested design would be to whatever extruder was used to determine the values given in the code, I wanted another way to determine this value. These calculations are used as a check to avoid wasting plastic and the possibility of breaking the extruder first time.

To simplify things I chose to calculate the parameter by treating the filament and melted plastic as an incompressible fluid. This simplification should hold true once the system is in a steady state extruding plastic. If no material leaks out, is absorbed into the body of the extruder itself, or is vaporized, then logically the volume of material that goes into the system must also leave the system. This also assumes that there is no thermal expansion or any change in density of the material (which is what I really mean when I say “incompressible”). Figure 2 below shows a diagram of my greatly simplified model of the system.

Figure 2: Model of the simplified system shown with the extrusion chamber shown as a grey box.

Bear in mind that I’m not a mechanical or chemical engineer. I’m an electrical engineer, so my calculations and assumptions might be way off. If anyone knows a better way to model and or calculate this sort of thing, I’d love to hear from you.

What goes in, must come out

Calculating the volume of filament entering the extruder:

The volume that goes into the extruder is the cross sectional area of filament multiplied by the length fed in. This assumes the filament is a perfect cylinder.

rf = 0.5 × Øf (filament diameter)
CSAf(filament cross sectional area) = π × rf²
volf (filament volume per revolution) = CSAf × lenf

Assuming the filament is moving at the same speed as the outermost point on the teeth of the drive gear or surface of the pinch wheel then the length of filament fed in should be the same as the linear distance travelled by the outside of the pinch wheel. This is assuming that no slippage, no compression, and no deformation of the filament occur.

A point on the outside of the pinch wheel travels a linear distance of one circumference per revolution. This is calculated by π multiplied by the diameter.

ØPW (pinch wheel diameter)
lenf (filament length per revolution) = π × ØPW

The total volume entering the extruder per step is easily calculated by dividing the volume per revolution by the number of steps per revolution. If a gear reduction is used between the stepper and then the steps per revolution are multiplied by this ratio.

RM:PW (ratio of motor rpm to pinch wheel rpm)
steps360 (motor steps per revolution)

step-volf (filament volume per step) = volf
steps360 × RM:PW

Real world examples

In addition to the standard extruder I have also built a version of Adrian’s geared extruder. My version of this is shown in Figure 3 below. The ratio for this gear reduction is 5:1. I will compare this with the standard splined shaft extruder in the calculations below.

Figure 3: A geared extruder. The large gear has 55 teeth and the small gear has 11 giving a 5:1 reduction.

Using filament with a nominal diameter of 3mm gives:

rf = 0.5 × Øf = 1.5mm
CSAf = π × rf² = 7.07mm²

For a standard reprap using a 5mm diameter splined shaft:

ØPW = 5.0 mm
lenf = π × ØPW = 15.7mm
volf = CSAf × lenf = 111mm³

With a 200 steps per revolution stepper motor, driven using half steps and directly driving the pinch wheel:

RM:PW = 1

step-volf = 111mm³
400 × 1

step-volf = 0.278mm³

Alternatively, with the same motor driven using half steps but driving via a 5:1 gear reduction and a 8mm brass insert:

ØPW = 8.0 mm
lenf = π × ØPW = 25.1mm
volf = CSAf × lenf = 177mm³
RM:PW = 5

step-volf = 177mm³
400 × 5

step-volf = 0.0885mm³

This means that for each half step of the motor the original extruder draws in 0.278mm³ of plastic. If the geared extruder is used then the volume of plastic drawn in with each step of the motor is an order of magnitude lower at 0.0885mm³. This means that the geared extruder should have much finer control as well as increased torque.

Calculating the length of extrusion leaving the extruder:

The output of the system is via a smaller diameter nozzle. Working from our main assumption, what goes in must come out, the system must extrude all of the material entering the system but with a smaller cross sectional area.

Assuming the extrusion is the same as the nozzle diameter:

Øe (extrusion diameter) = ØNOZZLE
re (extrusion radius) = 0.5 × Øe
CSAe (cross sectional area of extrusion) = π × re²

Reworking our formulas from above gives us the length from a known volume. This assumes the extrusion, like the filament, is also a perfect cylinder.

lene (length of extrusion)
vole (extrusion volume ) = lene × CSAe

lene = vole

Substituting in the known volume drawn into the system per step:

vole = step-volf

lene = step-volf

This is the length of extrusion output per step. By inverting this we get the number of steps per mm of extrusion required by the software.


Real world examples (continued from above)

A nozzle with a 0.5mm hole and using a splined 5mm shaft direct drive system from above:

Øe = 0.5mm
step-volf = 0.278mm³ (from earlier example above)
re = 0.5 × Øe = 0.25mm
CSAe = π × re² = 0.196mm²

E_STEPS_PER_MM = 0.196mm²

E_STEPS_PER_MM = 0.705

A nozzle with a 0.5mm hole and using a 5:1 gear reduction and a 8mm brass insert from above:

Øe = 0.5mm
step-volf = 0.0885mm³ (from earlier example above)
re = 0.5 × Øe = 0.25mm
CSAe = π × re² = 0.196mm²

E_STEPS_PER_MM = 0.196mm²


These values match up well with the values for the standard extruders given in the firmware.

Tying it all together

After reading through the working above you can see the result is proportional to the ratio of the square of the diameters and some of the terms will cancel out.

Combining and simplifying gives a single formula:

E_STEPS_PER_MM = ØNOZZLE² × steps360 × RM:PW
Øf² × π × ØPW


Øf =  filament diameter
ØPW =  pinch wheel diameter
ØNOZZLE =  nozzle diameter
steps360 =  number of steps per revolution
RM:PW =  ratio of motor revolutions to pinch wheel revolutions

Below in Listing 1 are my changes to configuration.h to automatically calculate the steps per mm based on the characteristics of the extruder. This is calculated when the main firmware is compiled. I’ve also created a google spreadsheet here to help calculate the same value if you just want the number to throw straight into configuration.h. These values form a good starting point for the inevitable fine-tuning which is required later.

// calculated assuming non-compressible fluid and perfect 
// cylinder filament and extrusion etc so tweaks probably be
// needed. basically what volume goes in must come out.
#define FD 3.0   // = filament diameter (3mm)
#define ED 0.5   // = extruded diameter (0.5mm)
#define PWD 8.0  // = pinch wheel diameter, outer diameter 
                 //  of teeth on my custom brass knurled gear
                 //  (7mm)  or brass insert (8mm)
#define GEAR_RATIO 5 // = drive gear ratio (1 normally,
                     //   5 for 5:1 geared drive)
#define E_STEPS_PER_REV 400 // number of steps per revolution 
                            // (I'm using a half steps drive)

#define PI 3.14159265359                                //*RO
#define FL (PI * PWD) // filament length fed in one rev //*RO
#define R1 (FD/2)     // radius of filament going in    //*RO
#define R2 (ED/2)     // radius of extruded filament    //*RO
#define EL ( (FL*R1*R1)/(R2*R2) ) // length extruded    //*RO


Listing 1: Automated calculations added to configutaion.h

Any thoughts, comments and ideas are always welcome.

How to fix LCD contrast problems on the AVR Butterfly

I had heard a number of reports of contrast problems on the AVR Butterflies produced over the last year. Unfortunately I haven’t had an opportunity to look into this until now. This week while checking some MP3 player code on a new AVR Butterfly I ran into the problem for myself. Luckily it was a simple fix.

A quick search on AVR Freaks turned up the following solution.

Turns out adding the following to lcd_driver.c fixed the problem:

//updated 2006-10-10, setting LCD drive time to 1150us in FW rev 07,
//instead of previous 300us in FW rev 06. Due to some variations on the LCD
//glass provided to the AVR Butterfly production.
LCDCCR |= (1<<LCDDC2) | (1<<LCDDC1) | (1<<LCDDC0);

I inserted these lines as instructed into the file LCD_driver.c at the end of the LCD_Init() function just after gLCD_Update_Required = FALSE; (this is around line 170 for ButterflyMP3).

The change has fixed the problem on my hardware. Although I have updated the files in the CVS, I have not done a general release for either ButterflyMP3 or Butterfly Logger. You can get the modified files directly from the CVS system, here for ButterflyMP3 and here for Butterfly Logger

In addition to the changes given above in the post at AVR Freaks, the macro around line 45 in the file LCD_driver.h

#define LCD_CONTRAST_LEVEL(level) LCDCCR=(0x0F & level)

should be changed to something like

#define LCD_CONTRAST_LEVEL(level) LCDCCR=(0xF0 & LCDCCR) | (0x0F & level)

This change will ensure that the drive time setting does not get erased when using the macro to set the contrast at some time after initialisation. I have not tested this as none of my firmware sets the contrast after startup.

Why are these changes needed?

It appears that the LCD’s characteristics have changed and it now requires a much longer drive time of 1150μs. The original LCD needed only 330μs. I’m unsure of the effect of this on the overall power consumption other than it will increase the LCD’s portion of it.

If the original 330μs drive time is used, then some of the newer LCDs will be very dim and you may only be able to read them at an angle if at all. If you really want to squeeze a little more battery life out of your AVR Butterfly based project then you could have a go at tweaking this value back to 850μs or even 575μs and check the display for readability.

Heres an explanation of what those bits do from the ATMEGA169PA data sheet on page 248.

• Bits 7:5 – LCDDC2:0: LDC Display Configuration
The LCDDC2:0 bits determine the amount of time the LCD drivers are turned on for each voltage transition on segment and common pins. A short drive time will lead to lower power consumption, but displays with high internal resistance may need longer drive time to achieve satisfactory contrast. Note that the drive time will never be longer than one half prescaled LCD clock period, even if the selected drive time is longer. When using static bias or blanking, drive time will always be one half prescaled LCD clock period.

And the accompanying table from the same datasheet also on page 248.

Table 23-7. LCD Display Configuration

LCDDC2LCDDC1LCDDC0Nominal drive time
000300 µs
00170 µs
010150 µs
011450 µs
100575 µs
101850 µs
1101150 µs
11150% of clkLCD_PS

Arduino MP3 Shield Rev B (correct files this time)

I have to apologise to everyone who downloaded the Revision B Arduino MP3 Shield PCBs when I posted them here and here.

I accidently uploaded and emailed everyone the wrong files, not only were they not revision B they weren’t even revision A. This was totally my fault as I grabbed the files from the wrong directory when uploading. I was sure I had checked they were correct but obviously I hadn’t.

I’ve included some pictures of what the layout is supposed to look like for Rev B so you can check the gerbers and EAGLE files are correct before getting any made. ( Look for the large ‘B’ on the top copper layer.)

Figure 1 : Arduino MP3 Shield Rev B PCB - Top view

Figure 2 : Arduino MP3 Shield Rev B PCB - Bottom view

I have since fixed the links and are now hosting the correct files. Here are the links again:

EAGLE files are here

GERBER files are here

Arduino Library files are here

PDF of the schematic is here

Once again sorry to everyone who got the wrong files.

Creative Commons License
Arduino MP3 Shield by Nick Lott is licensed under a Creative Commons Attribution-Share Alike 2.0 UK: England & Wales License.
Based on a work at www.brokentoaster.com.

MAX6675 Thermocouple breakout board (TC-6675)

In order to improve and verify the performance of my Reprap Mendel extruder I decided to build a system based on a K-Type thermocouple. I ordered some samples of a MAX6675 from Maxim Integrated Products. I decided to use this chip over the Analog Devices AD595 out of curiosity as it was new to me and due to the 12-bit digital output. Although plenty of breakout boards already exist for this product I decided to design my own based around this PCB-mount thermocouple connector. I like the idea of being able to simply unplug a thermocouple and quickly plug another one in so the use of a standard miniature socket sounds like a pretty good idea to me. It makes switching between probes a breeze and allows me to use cheap thermocouple probes from Maplin

I haven’t tested this board yet so I’ll have to update the post later after I’ve had a chance to play with some ferric chloride.

Having stood back from the design for about 3 seconds it occurs to me that I should probably combine this with my ATTINY45 USB Key project to make a standalone Thermocouple to USB converter, but who has the time 🙂 .

All files are Creative Commons Attribution-Share Alike 2.0
TC-6675, Schematic (PDF)
Eagle Library (MAX6675ISA+ and RS#3817564)
TC-6675 Schematic (EAGLE)
TC-6675 PCB layout (EAGLE)

Getting more from your thermistor lookup tables

Last weekend I managed to blow up the motor driver chips on the reprap mendel extruder board. I did this by enabling the #FASTPWM option in the extruder firmware. I’m not sure exactly why this caused the chips to blow up, but what I do know is that the smoke got out and now they don’t do anything. I believe the problem was that either the PWM wasn’t working at all or that the current in the motor windings was not able to decay enough in the time allowed for by the faster PWM frequency. I have been investigating using a standard v2.3 stepper motor board to drive the extruder motor but I will need to modify the motherboard firmware to get it working properly.

While poking about in the extruder firmware I noticed that the thermistor lookup tables could be improved. Currently I have been extruding my PLA at 250°C for reliable results. Looking at the standard lookup table, 255 is the second entry which means that most of the look up table is spent defining temperatures much below where I really want the most accurate temperature readings.

By choosing the points more carefully and tailoring the spread we can achieve better accuracy where we want it. This is most clearly seen when the table is plotted.

Figure 1 : Comparison of thermistor lookup tables

Figure 1 above shows a hand modified table as compared to both a perfect table with 1023 values and the standard table. If you look at the difference between the perfect line and the standard line you can see that above 255°C the graphs diverge significantly. What does this mean? It means that an actual temperature of 270°C will appear to our controller as 400°C. While this probably won’t make a large difference to the control of temperatures below 250°C it would be nice to know the system is accurate right up to 300°C. Changing the table may also improve the response of the control system near 250°C and reduce unexpected behaviour. An example of this might be when the system is set to 255°C and overshoots by only 5°C, the system will compensate for a measured overshoot of 20°C.

The original (ADC prioritised) table A temperature prioritised table
   {1, 841},
   {54, 255},
   {107, 209},
   {160, 184},
   {213, 166},
   {266, 153},
   {319, 142},
   {372, 132},
   {425, 124},
   {478, 116},
   {531, 108},
   {584, 101},
   {637, 93},
   {690, 86},
   {743, 78},
   {796, 70},
   {849, 61},
   {902, 50},
   {955, 34},
   {1008, 3}
   {5, 500},
   {6, 474},
   {8, 448},
   {9, 422},
   {12, 396},
   {15, 370},
   {20, 344},
   {26, 318},
   {35, 292},
   {49, 266},
   {70, 240},
   {103, 214},
   {155, 188},
   {236, 162},
   {359, 136},
   {526, 110},
   {711, 84},
   {867, 58},
   {962, 32},
   {1005, 6}

Table 1 : Comparison of thermistor lookup tables

The Python script used to generate these lookup tables first generates an even spread of ADC values that the software will see and then generates the corresponding temperatures. While this approach generally gives a great conversion across the entire range of readings, it’s not the best use of the lookup table for this particular application. I’ve edited the script to reverse the process and select a number of temperature points where we are interested in and then look up the corresponding ADC values to match. This results in a table (shown in Table 1 above) that ensures the 200°C to 300°C range has plenty of points and the sub 100°C region has the minimum required. The modified Python script is here.

Unfortunately I broke my last thermistor while building a new extruder heater so I haven’t been able to test the new table. When I get a replacement I plan to check the results with a thermocouple to ensure accuracy across the range.