Hi all,
I've benefitted enormously from all the hard work done by everyone in this thread and have built several clone dataloggers successfully following the information here. To give back in some small way I thought I'd do a quick how-to writeup for those who may read this but are too intimidated by the technical aspects to give it a try. It's really not too hard!
What You'll NeedParts:
belfryboy's PCBs from OSH Park (3 for $6.20)
From Mouser you'll need per datalogger:1 USB Cable (eg. Mouser #538-88738-8200) $2.91 each
1 PCB Header (798-DF11-20DS-2DSA05) $1.71 each
1 AT45DB011 (556-AT45DB011D-SSH-B) $1.05 each
1 10uF tantalum Case 'A' capacitor (647-F931A106MAA) $0.181 each
2 0.1uF ceramic capacitors (581-08055C104K) $0.121 each, $0.242 total)
1 FTDI FT232RL Serial-USB Interface (895-FT232RL) $5.43 each
So in Canadian dollars the total cost for parts is $17.72, assuming you just make the one and order no extras. If you made all 3 you'd be around $13.58 each. Quite a bit better than the $165 or so Davis lists the WeatherLink USB at!
I would recommend ordering extra AT45s at a minimum, though, as they're probably the most likely thing to mess up.
Tools & Supplies:
Soldering Iron with fine tip
Decent quality fine solder (eg.
0.031" Kester 60/40)
Decent flux (eg.
SRA #312 Flux Pen)
Arduino (eg.
Arduino Uno)
Some wire for connecting between AT45s and Arduino
Optional, SOIC8 Breakout Board or SOIC8 to DIP (if using Arduino with breadboard)
Optional, but recommended - an
anti-static wrist bandOptional, but recommended if your hand is shaky like mine - An SMD rework station
I use an Aoyue 968A+ rework station/soldering iron with Hakko tips and find it works well
Programming the AT45DB011DIf you are using a breakout board or a SOIC-DIP adapter solder the AT45 to the board.
Edit/update Or buy a SOIC8 clip from aliexpress and use that to clip on the chip. Much easier.
End edit Soldering SMDs is a topic of it's own, so if you need help just google it. I have neural issues and I shake quite a bit so I prefer to flux and tin the pads on the board first, then use an SMD rework heat gun to heat the solder and board while I use a set of tweezers to move the chip in place.
Alternatively, if you are using only wires, you can solder them directly to the pins of the AT45. You should tin the wire and use the minimum amount of solder possible to attach the wires. Check each one to make sure it's firmly attached (pull gently) and not touching it's neighbour.
Connect the other end of the wires to the Arduino. The pins of the AT45 are numbered like this:
1 8
2 7
3 6
4 5
If you look really carefully there'll be a tiny triangle or dimple at the #1 pin side (usually next to #1 pin). These pins connect to the Arduino as follows:
AT45DB011D ------------ ARDUINO
1 (SI/MOSI) ------------ Pin 11
2 (SCK) ------------ Pin 13
3 (RESET) ------------ Pin 8
4 (CS) ------------ Pin 10
5 (WP) ------------ Pin 7
6 (VCC) ------------ 3.3V
7 (GND) ------------ Ground
8 (SO) ------------ Pin 12
Once everything's connected open the Arduino application and open the Davis_Weatherlink_Programmer sketch attached to this post, or copy/paste the following into a blank sketch:
#include <SPI.h>
/* This code by default reads the 64 byte unique Device Identifier on
the attached AT45DB001D, outputs it to the console, and then calculates
the 64 bytes to be programmed in the Security Register to match a
genuine Davis Weatherlink interface, and outputs those 64 bytes
to the console. By uncommenting the last section in the setup() loop
you can choose to program the Security Register. But first make sure that
the read was successful and the calculated bytes are correct before enabling
writing.
*******
NOTE: YOU ONLY GET ONE CHANCE TO WRITE THE SECURITY REGISTER!
So make sure communication with the AT45 is OK or you'll have a dead
dataflash on your hands!
*******
*/
//DataFlash Commands
#define DATAFLASH_READ_MANUFACTURER_AND_DEVICE_ID 0x9F
#define DATAFLASH_READ_SECURITY_REGISTER 0x77
#define DATAFLASH_STATUS_REGISTER_READ 0xD7
#define DATAFLASH_CHIP_ERASE_0 0xC7
#define DATAFLASH_CHIP_ERASE_1 0x94
#define DATAFLASH_CHIP_ERASE_2 0x80
#define DATAFLASH_CHIP_ERASE_3 0x9A
#define DATAFLASH_READ_SECURITY_REGISTER 0x77
#define DATAFLASH_PROGRAM_SECURITY_REGISTER_0 0x9B
#define DATAFLASH_PROGRAM_SECURITY_REGISTER_1 0x00
#define DATAFLASH_PROGRAM_SECURITY_REGISTER_2 0x00
#define DATAFLASH_PROGRAM_SECURITY_REGISTER_3 0x00
//As per Watson's work described at http://www.wxforum.net/index.php?topic=18110.msg200376
int const GreenDot_Table[256] =
{
0x00, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x18, 0x1C, 0x21, 0x25, 0x29, 0x2D, 0x31, 0x35, 0x39, 0x3D,
0x46, 0x42, 0x4E, 0x4A, 0x56, 0x52, 0x5E, 0x5A, 0x67, 0x63, 0x6F, 0x6B, 0x77, 0x73, 0x7F, 0x7B,
0x8C, 0x88, 0x84, 0x80, 0x9C, 0x98, 0x94, 0x90, 0xAD, 0xA9, 0xA5, 0xA1, 0xBD, 0xB9, 0xB5, 0xB1,
0xCA, 0xCE, 0xC2, 0xC6, 0xDA, 0xDE, 0xD2, 0xD6, 0xEB, 0xEF, 0xE3, 0xE7, 0xFB, 0xFF, 0xF3, 0xF7,
0x18, 0x1C, 0x10, 0x14, 0x08, 0x0C, 0x00, 0x04, 0x39, 0x3D, 0x31, 0x35, 0x29, 0x2D, 0x21, 0x25,
0x5E, 0x5A, 0x56, 0x52, 0x4E, 0x4A, 0x46, 0x42, 0x7F, 0x7B, 0x77, 0x73, 0x6F, 0x6B, 0x67, 0x63,
0x94, 0x90, 0x9C, 0x98, 0x84, 0x80, 0x8C, 0x88, 0xB5, 0xB1, 0xBD, 0xB9, 0xA5, 0xA1, 0xAD, 0xA9,
0xD2, 0xD6, 0xDA, 0xDE, 0xC2, 0xC6, 0xCA, 0xCE, 0xF3, 0xF7, 0xFB, 0xFF, 0xE3, 0xE7, 0xEB, 0xEF,
0x31, 0x35, 0x39, 0x3D, 0x21, 0x25, 0x29, 0x2D, 0x10, 0x14, 0x18, 0x1C, 0x00, 0x04, 0x08, 0x0C,
0x77, 0x73, 0x7F, 0x7B, 0x67, 0x63, 0x6F, 0x6B, 0x56, 0x52, 0x5E, 0x5A, 0x46, 0x42, 0x4E, 0x4A,
0xBD, 0xB9, 0xB5, 0xB1, 0xAD, 0xA9, 0xA5, 0xA1, 0x9C, 0x98, 0x94, 0x90, 0x8C, 0x88, 0x84, 0x80,
0xFB, 0xFF, 0xF3, 0xF7, 0xEB, 0xEF, 0xE3, 0xE7, 0xDA, 0xDE, 0xD2, 0xD6, 0xCA, 0xCE, 0xC2, 0xC6,
0x29, 0x2D, 0x21, 0x25, 0x39, 0x3D, 0x31, 0x35, 0x08, 0x0C, 0x00, 0x04, 0x18, 0x1C, 0x10, 0x14,
0x6F, 0x6B, 0x67, 0x63, 0x7F, 0x7B, 0x77, 0x73, 0x4E, 0x4A, 0x46, 0x42, 0x5E, 0x5A, 0x56, 0x52,
0xA5, 0xA1, 0xAD, 0xA9, 0xB5, 0xB1, 0xBD, 0xB9, 0x84, 0x80, 0x8C, 0x88, 0x94, 0x90, 0x9C, 0x98,
0xE3, 0xE7, 0xEB, 0xEF, 0xF3, 0xF7, 0xFB, 0xFF, 0xC2, 0xC6, 0xCA, 0xCE, 0xD2, 0xD6, 0xDA, 0xDE
};
/* Pin connection definitions. Pin connections between Arduino and Dataflash
should be as follows:
AT45DB011D ------------ ARDUINO
1 (SI/MOSI) ------------ Pin 11
2 (SCK) ------------ Pin 13
3 (RESET) ------------ Pin 8
4 (CS) ------------ Pin 10
5 (WP) ------------ Pin 7
6 (VCC) ------------ 3.3V
7 (GND) ------------ Ground
8 (SO) ------------ Pin 12
Note that pin #s on AT45DB are as follows:
1 8
2 7
3 6
4 5
A breakout board for the SOIC package makes connections easier but isn't
strictly necessary
*/
int csPin = 10;
int resetPin = 8;
int writeProtectPin = 7;
int delayTime = 500; //half-second typical delay
void setup(){
//Initial setup & SPI Initialization
pinMode(csPin,OUTPUT);
SPI.begin();
SPI.setDataMode(SPI_MODE3);
SPI.setBitOrder(MSBFIRST);
SPI.setClockDivider(SPI_CLOCK_DIV2);
Serial.begin(19200);
delay(delayTime*4); //wait 2 seconds before continuing
//Begin communication tests
Serial.println("Attempting communication with DataFlash...");
//First we get the current status register
uint8_t STATUS;
digitalWrite(csPin,LOW);
SPI.transfer(DATAFLASH_STATUS_REGISTER_READ);
STATUS=SPI.transfer(0);
digitalWrite(csPin,HIGH);
Serial.print("Status Register: 0b");
Serial.println(STATUS,BIN);
Serial.println("Format: RDY MEMCOMP 0 0 1 1 SECPROT PAGESZ");
delay(delayTime);
//Next Manufacturer ID and Device ID
byte MANUFACTURER_DEVICE_ID[4];
digitalWrite(csPin,LOW);
SPI.transfer(DATAFLASH_READ_MANUFACTURER_AND_DEVICE_ID);
for (int i=0;i<4;i++){
MANUFACTURER_DEVICE_ID[i]=SPI.transfer(0);
}
digitalWrite(csPin,HIGH);
//Output result
Serial.print("Manufacturer ID (Should be 0x1F): ");
Serial.println(MANUFACTURER_DEVICE_ID[0],HEX);
Serial.print("Device ID byte1 (should be 0x22): ");
Serial.println(MANUFACTURER_DEVICE_ID[1],HEX);
Serial.print("Device ID byte2 (should be 0x00): ");
Serial.println(MANUFACTURER_DEVICE_ID[2],HEX);
Serial.print("Length of Extended Device Info: ");
Serial.println(MANUFACTURER_DEVICE_ID[3],HEX);
delay(delayTime);
/*Security Register
The Security Register consists of two 64-byte parts, composing a total
of 128 bytes. Bytes 0-63 are one-time user programmable. Bytes 64-127
are programmed at factory and can't be changed. As outlined at
http://www.wxforum.net/index.php?topic=18110.0 Davis calculates the first
64 bytes based on an algorithm that uses the second 64 bytes and the values
in the GreenDot_Table above. Here we will read the 64 factory-programmed bytes
and calculate what should be programmed for the other 64 bytes.
*/
byte FACTORY_SECURITY_REGISTER[64]; //Factory-programmed
byte USER_SECURITY_REGISTER[64]; //Calculated by us
//We need to read the entire 128 bytes in one shot.
//Start with verifying the first 64 bytes are unwritten
Serial.println("Reading Security Register...");
digitalWrite(csPin,LOW);
SPI.transfer(DATAFLASH_READ_SECURITY_REGISTER);
SPI.transfer(0x00);//dummy 1
SPI.transfer(0x00);//dummy 2
SPI.transfer(0x00);//dummy 3
Serial.println("Checking that Programmable Register is empty (0xFF)...");
for (int i=0;i<64;i++){
uint8_t CURRENT_BYTE;
CURRENT_BYTE=SPI.transfer(0x00);
if (CURRENT_BYTE != 0xFF){
Serial.print("Error! Byte #");
Serial.print(i);
Serial.println(" is not 0xFF!!");
}
}
// Read Factory programmed 64 Byte security Register
Serial.println("Reading Factory-programmed Security Register..,.");
for(int i=0;i<64;i++){
int byteNumber = i+64;
Serial.print(byteNumber);
Serial.print(" - 0x");
FACTORY_SECURITY_REGISTER[i]=SPI.transfer(0x00);
Serial.println(FACTORY_SECURITY_REGISTER[i],HEX);
}
digitalWrite(csPin,HIGH);
delay(delayTime);
// Calculate User-programmable security bytes
Serial.println("Calculating User-Programmable Security Register...");
// The first 3 bytes can be anything. Davis uses it as a serial number
USER_SECURITY_REGISTER[0]=0x01; //I just used 1, 2, 3... Change as you like
USER_SECURITY_REGISTER[1]=0x02;
USER_SECURITY_REGISTER[2]=0x03;
/* Calculate the remaining 61 bytes
This is taken verbatim from Watson's post, above. location is the location
in the green dot table that the value we need at position i is located.
Because we're using an 8-bit unsigned integer when the calculation below for
location is greater than 255 it loops around and starts at 0 again.
*/
uint8_t location,i;
for ( i=3; i<64; i++){
Serial.print("Byte #");
Serial.print(i);
location = (uint8_t)FACTORY_SECURITY_REGISTER[i]+i;
Serial.print(" Location in Table: ");
Serial.print(location,DEC);
USER_SECURITY_REGISTER[i] = GreenDot_Table[location];
Serial.print(" Value: 0x");
Serial.println(USER_SECURITY_REGISTER[i],HEX);
}
delay(delayTime);
//Uncomment (remove the /* at the start and */ at the end) the lines
//below to program the Security Register
/*
boolean wait = true; //Make sure chip isn't busy
while (wait == true){
uint8_t STATUSREG;
uint8_t NOTBUSY;
digitalWrite(csPin,LOW);
SPI.transfer(DATAFLASH_STATUS_REGISTER_READ);
STATUSREG=SPI.transfer(0);
digitalWrite(csPin,HIGH);
NOTBUSY= getBit(STATUSREG, 7);
if (NOTBUSY == 1){
wait=false;
}
}
digitalWrite(csPin,LOW);
Serial.println("Programming security register...");
SPI.transfer(DATAFLASH_PROGRAM_SECURITY_REGISTER_0);
SPI.transfer(DATAFLASH_PROGRAM_SECURITY_REGISTER_1);
SPI.transfer(DATAFLASH_PROGRAM_SECURITY_REGISTER_2);
SPI.transfer(DATAFLASH_PROGRAM_SECURITY_REGISTER_3);
for(int i=0;i<64;i++){
Serial.print("Programming Byte ");
Serial.println(i);
SPI.transfer(USER_SECURITY_REGISTER[i]);
}
digitalWrite(csPin,HIGH);
//Verify everything wrote out OK
wait=true; //Make sure chip isn't busy
while (wait == true){
uint8_t STATUSREG;
uint8_t NOTBUSY;
digitalWrite(csPin,LOW);
SPI.transfer(DATAFLASH_STATUS_REGISTER_READ);
STATUSREG=SPI.transfer(0);
digitalWrite(csPin,HIGH);
NOTBUSY= getBit(STATUSREG, 7);
if (NOTBUSY == 1){
wait=false;
}
}
delay(delayTime);
Serial.println("Verifying everything went OK...");
byte VERIFY_SECURITY_REGISTER[128];
digitalWrite(csPin,LOW);
SPI.transfer(DATAFLASH_READ_SECURITY_REGISTER);
SPI.transfer(0x00);//dummy 1
SPI.transfer(0x00);//dummy 2
SPI.transfer(0x00);//dummy 3
for(int i=0;i<128;i++){
VERIFY_SECURITY_REGISTER[i]=SPI.transfer(0x00);
}
digitalWrite(csPin,HIGH);
boolean writeOk = true;
for (int i=0;i<64;i++){
if (USER_SECURITY_REGISTER[i]!=VERIFY_SECURITY_REGISTER[i]){
Serial.print("Error at security register ");
Serial.print(i);
Serial.print(": Should be 0x");
Serial.print(USER_SECURITY_REGISTER[i],HEX);
Serial.print(". Is 0x");
Serial.print(VERIFY_SECURITY_REGISTER[i],HEX);
Serial.println(".");
writeOk = false;
}
}
if (writeOk == true){
Serial.println("Looks like everything went great!");
}
else{
Serial.println("Oh no! There was a problem programming the security register!!");
}
*/
}
void loop(){
}
uint8_t getBit(uint8_t bits, uint8_t pos){
return (bits >> pos) & 0x01;
}
Upload the sketch to the Arduino, then open the Serial Monitor from the Arduino application. You should see the results of some tests, including a status read (which would typically output 10001100), a check to make sure that the programmable register is empty/not programmed (should read all 0xFF, if not there's a problem with the chip or your connections), output of the factory security register, and a calculation of bytes 3-63 that we have to program based on the factory security register (as per Watson's observation bytes 0-2 are a serial number and can be set to anything).
You should get no errors and the output should be repeatable (ie. if you click the Reset button on top of the Arduino you should get the exact same output every time). If you get errors check all your connections from the Arduino to the AT45. As per iBangkok24's observation try the other ground on the Arduino board to make sure that's not the problem.
Once everything appears OK you can uncomment (remove the /* and */) the section at the end of the code that deals with programming the security register. Upload the program again. This time the security register will be programmed, and afterwards it'll be verified. Out of 8 chips I programmed one didn't work. It refused to change one bit from 0xFF, likely due to either a defective chip or me damaging it with overheating or static electricity.
Once the chip is programmed you can remove it from the wires/breakout board.
Assembling everythingTin the pads on the belfryboy board (EXCEPT the header tabs and possibly the U1 and IC1 pads if using a soldering iron, depending on technique). Solder the 0.1uF capacitors to the places marked C1 and C2, being careful not to solder them together or solder the pads together. Solder the 10uF tantalum capacitor to C3, with the grey stripe pointing towards U1's location (make sure this is installed the right way around as tantalum capacitors are polarized and will be destroyed if installed backwards).
Solder the FTDI RS232RL to the U1 location. Note that on the top of the chip there's a tiny dimple at pin one. That dimple should be located where the o is silkscreened on the board. A rework station really comes in handy for soldering this high of a pin density chip unless you're really steady or really patient. If you don't have a rework station solder just one pin so that the chip is in the proper orientation/location. Then tack one of the pins on the opposite side. Once that's done solder the rest of the pins. Google SMD soldering for more examples of technique.
Solder the AT45 to IC1 location. Note again that a o is silkscreened at pin 1 location. Recall that we already identified pin one of the AT45 by the triangle and/or dimple located on the top of the chip. Align the board and chip correctly and solder into place.
Insert the header from the BACK of the board (where the white rectangular outline is). Looking at the header from the hole side the pins are numbered like this:
2 4 6 8 etc.
1 3 5 7
A careful look at the long side of the header near the bottom where the pins extend out of it you should see a triangle pointing upward at pin one. See
page 107 of this pdf for more details.
Pin 1 should insert into the hole with the white square outlining it on the PCB. Push all the way in till it clicks. Then solder the pins from the other side of the board, taking care not to short any to each other.
Finally, solder the USB cable to USB header. The red wire goes to Vbus. White is D-. Green is D+. The two black wires (one is actually bare wire with a black sleeve over it) go to Gnd.
Finishing it upOnce you're done wiring you can pot the board with Techspray or epoxy, or wrap it in electrical tape, or just leave it as-is. At your Davis console unplug power and remove the batteries. Insert your new logger all the way into the port and route the wire appropriately. Re-install the batteries and the cover and plug power back in. Plug the USB cable into your PC and proceed to set it up as
outlined by belfryboy.
If you're using linux, you can use minicom to test communication (as
outlined here), although I never found that worked right for me the first time. Instead, I installed weewx and ran wee_config_vantage as
outlined here to get things working.
Hopefully that helps someone out. I realize this isn't new information but after spending several hours figuring things out I thought I'd try to simplify it for the next person who comes along.
Thanks again everyone for the service you've done in helping restart 3rd party dataloggers!!