You buy a bunch of MPC3002 ADCs because they're cheap and easy. SPI! ESP8266 does SPI, right? So they show up, you bust it out and hook up the SPI pins and open up http://www.ee.ic.ac.uk/pcheung/teaching/ee2_digital/MCP3002.pdf to learn how to talk to the thing. Page 15. Sweet.
Wait. Oh yeah. This thing said 10-bit resolution over SPI. Coolcoolcoolcool. Nodoubtnodoubtnodoubt. Umm...
At least the packet diagram is crystal clear. So 4 bits for command info and then 11 bits for data.
Side-note: I really love that all the diagrams say "Don't Care" for the remaining input bits in the transaction. Thanks, Microchip.
The most difficult bit of all this with the ESP8266 is getting the SPI clock mode and speed and all that stuff to sync up. ESP8266 is faster than an ATMega, so it doesn't always work out like I hope.
So without further fanfare, here's how to read that packet.
Hint: Just cheat and use a union!
#include <SPI.h>
/**
- Every ESP8266 device is different but for NodeMCU and HUZZAH apparently
7280000 is the sweet spot.
- MSBFIRST is the default and figures 6-1 and 6-2 in the docs assume it anyway.
- MODE0 means low low clock mode. We're hoping to catch the bits on the falling edge of the oscillation.
Learn about some SPI clocks. It's interesting stuff.
*/
SPISettings spiSettings(7280000, MSBFIRST, SPI_MODE0);
const int SPI_CS = 15;
void setup() {
// we'll talk to this pin to let the SPI know when we're talking.
pinMode (SPI_CS, OUTPUT);
// this gets things all clocky
SPI.begin();
SPI.setBitOrder(MSBFIRST);
SPI.setFrequency(7280000);
// blahblahblah whatever else you need to do for your stuff
}
void loop() {
// So we're gonna start chatting with the device
SPI.beginTransaction(spiSettings);
// HEY, DEVICE, HOPE YOU'RE READY!
digitalWrite (SPI_CS, LOW);
/**
Okay this is rather specific. Remember that "Don't Care" part I
mentioned? That means we have to front-load the command bits we'll be
sending to the SPI inside an int8_t. 0b notation works great for
cheating on this. Page 13 of the manual for the MCP3002 explains
this in a much longer form than I do here.
bit 1: Start -- Set it to 1 so that the SPI knows this is the start
bit 2: SGL/DIFF -- Tough to explain this. Just send a 1 here unless you know what the opposite does.
bit 3: ODD/SIGN -- Set to 1 for channel 1 or 0 for channel 0 on the ADC.
bit 4: Endian -- So Little or Big? We'll be sending 0 send it allows us to use "word" which does a lot of endian handling for us.
*/
uint8_t cmd = 0b11000000; // as in read channel 0
byte msb, lsb;
word assembled;
// now we'll just start pulling in the bits.
msb = SPI.transfer(cmd);
lsb = SPI.transfer(0);
// Ignore that null bit and apply a bit mask. It has a 10 bit resolution, so 10 1's at the end would do it.
assembled = word(msb, lsb);
Serial.println(assembled);
// HEY, DEVICE, I'M DONE TALKING. BYE!
digitalWrite (SPI_CS, HIGH);
SPI.endTransaction();
// This is just some processing I do for my own application. I left
// it in here as an example.
double reading = (1.0 - (assembled / 1024.0)) * 100.0;
}
I have a working example of this in my own project, so if there are any issues with it let me know. I'll update this, and I'll test to make sure my own project isn't derping.
EDITS: I discovered a lot of issues with the way I was parsing this stuff and realized that I was having clock issues. Tweaked those a bit and started moving bits around. The numbers I get with the updates seem more reliable, though I still have some doubts since I can't perfectly reconcile my approach with the docs.
FURTHER EDITS: I was overthinking this problem completely. Should have been using a `word` to handle the output this whole time.