Thursday, February 26, 2015

PART3 - I2C Driver - MCP9808 - Temperature Logger - ESP8266 CBDB

  As it was requested from you, my readers, in Part 3  of our CBDB saga, we will start building a simple, high precision, Temperature Data logger based on the MCP9808 chip, a ±0.5°C Maximum Accuracy Digital Temperature Sensor. As far as I know this is the first time NodeMCU float version I2C Driver for MCP9808.


     Features
     • Accuracy:
        - ±0.25 (typical) from -40°C to +125°C
       - ±0.5°C (maximum) from -20°C to 100°C
       - ±1°C (maximum) from -40°C to +125°C
  • User-Selectable Measurement Resolution:
       - +0.5°C, +0.25°C, +0.125°C, +0.0625°C
  • User-Programmable Temperature Limits:
       - Temperature Window Limit
       - Critical Temperature Limit
   • User-Programmable Temperature Alert Output
    • Operating Voltage Range: 2.7V to 5.5V
    • Operating Current: 200 μA (typical)
    • Shutdown Current: 0.1 μA (typical)
   • 2-wire Interface: I2C/SMBus Compatible
    • Available Packages: 2x3 DFN-8, MSOP-8

For more details, please see MCP9008 Datasheet

Don't be scared by the MSOP-8 package, with a small MSOP to DIP adapter will fit great in our CBDB extension slots it will not matter at all.

MSSOP8-DIP8 package adapter
 
What we will need:
  • CBDB Board
  • USB adapter (take a look on Part 1 for details how to connect them together)
  • MCP9808 Module from above
  • LED module (if you want maybe a LED ticker)

CBDB with MCP9808 Temperture module
CBDB with MCP9808 Temperature module + LED ticker

For programming and uploading the driver and the software we will continue to use the LuaUploader as before.

Driver implementation.

As MCP9808 has a I2C compatible compatible interface, building a driver for it it's a pretty straigh forward process:

1. Init I2C bus/interface
     
          dev_addr = 0x1F,
          init = function (self, sda, scl)
                   self.id = 0
           i2c.setup(self.id, sda, scl, i2c.SLOW)
          end

 

2. Read / Write to/from the desired register location.
  •  READ Register function
     read_reg = function(self, dev_addr, reg_addr)
          i2c.start(self.id)
          i2c.address(self.id, dev_addr ,i2c.TRANSMITTER)
          i2c.write(self.id,reg_addr)
          i2c.stop(self.id)
          i2c.start(self.id)
          i2c.address(self.id, dev_addr,i2c.RECEIVER)
          c=i2c.read(self.id,2)
          i2c.stop(self.id)
          return c
    end

  • READ Temperature function
    readTemp = function(self)
            h, l = string.byte(self:read_reg(0x1F, 0x05), 1, 2)
            h1=bit.band(h,0x1F)
            --check if Ta > 0C or Ta<0C
            Sgn = bit.band(h,0x10)             
            -- transform - CLEAR Sing BIT if Ta < 0C
            h2 = bit.band(h1,0x0F)
            tp = h2*16+l/16
            --END calculate temperature for Ta > 0
           return tp
    end


NOTE: This is for Tambient > 0°C only. Take a look in the  MCP9008 Datasheet . If you need it also on the negative temperature scale then you need to do some extra transformations as the temperature data is stored in the 16-bit read-only Ambient Temperature register Ta as 13-bit data in two’s complement format.

For testing, just save the code on ESP as 'mcp9808.lua', restart ESP and run:
           require('mcp9808')                 --call for new created MCP9808 Module Driver
           sda=2 --GPIO4                      --  declare your I2C interface PIN's
           scl=1 --GPIO5
          mcp9808:init(sda, scl)            -- initialize I2C    
          tp1 = mcp9808:readTemp()   -- read temperature
         =print(tp1)                              -- print it

 Let's put it together :

If all ok, we can move to the next step, how to make the data available on the web. 
For this one we will need 
WEB Server 
srv=net.createServer(net.TCP)
  srv:listen(80,
     function(conn)
           conn:on("receive",function(conn,payload) print(payload)
           conn:send("HTTP/1.1 200 OK\n\n")
           conn:send("<META HTTP-EQUIV=\"REFRESH\" CONTENT=\"5\">")
           conn:send("<html><title>MCP9808 - Temperature Log Server - ESP8266</title><body>")
           conn:send("<h1>ESP8266 Temperature Log Server - MCP9808</h1><BR>")
           conn:send("Temperature   : <b>" .. tp1 .. "&deg;C</b><BR><BR>")
           conn:send("Node.HEAP : <b>" .. node.heap() .. "</b><BR><BR>")
           conn:send("IP ADDR    : <b>".. wifi.sta.getip() .. "</b><BR>")
           conn:send("Node MAC   : <b>" .. wifi.sta.getmac() .. "</b><br>")
           conn:send("TMR.NOW    : <b>" .. tmr.now() .. "</b><BR<BR><BR>")
           conn:send("</html></body>")
           conn:on("sent",function(conn) conn:close() end)
      end)
end)
 READ Temperature function
        function readTMP()
            mcp9808:init(sda, scl)
            tp1 = mcp9808:readTemp()
            print(tp1)
        end

TIMER to establish how often we want the reading process to be done.
       -- read data every 1sec for direct web reading
       tmr.alarm(0, 1000, 1, function() readTMP() end )

 Save the code on ESP as 'web_temp.lua', restart ESP and run:  
        
        =wifi.sta.getip()              -- find the IP Address where your Web Server will be
        dofile("web_temp.lua")  -- Start the Web Server

 Open your favorite Web browser and type your nre Web Server IP address. If all ok, should look something like below :



So far so good. Next if you want the Web Server to start automatically when your CBDB module starts or reboots, then you neet to create and add some lines in your 'init.lua' file:

tmr.now()                         -- for debug only, you can skip it
wifi.sta.getmac()     
        -- for debug only, you can skip it       
wifi.sta.getip()
                -- for debug only, you can skip itnode.heap()
dofile("web_temp.lua")   -- needed to start Web Server and  Temperature logger


Save the code on ESP as 'init.lua', restart ESP. It should reboot and restart the program and reinitialize the Web Server.





That's all for today, thank you for your great feedback and looking forward for your suggestions!

23 comments:

Jean-Pierre G said...

Hi,

Thank you for sharing teh code.
I tried it but I always get 255 as output (for h and l values)

I wonder if it is due to the lack of pull up resistors on SDA and SCL

Any idea please ?
Thanks
JP

Tracker J said...

Hi Jean-Pierre, If you receive always 255 it's a problem with your I2C bus communication. Be sure you have in place proper pullup resistors and also check that SDA and SCL properly connected and defined for your setup.

Jean-Pierre G said...
This comment has been removed by the author.
Jean-Pierre G said...

Thank you for your reply.

I had at least two bugs :
GPIO0 and GPIO2 have now changed their numbering in lua's latest release.

and my device has not the 0x18 classical address but 0x48 (it was a sample device...)

Now seems to work (at least when adressing the device I get a "true" answer.)
Thank you

Tracker J said...

Good news, indeed. If you have proper communication with it on I2C the rest it's easy.

For the records: Accordigly with the datasheet for obtaining device I2C address (A6A5A4A3A2A1A0) you have 2 variants for address code - A6, A5, A4, A3 = 0x18 or 0x48 + Add your own choice of user selectable slave address with A2,A1,A0 pins and the result it's your device address.

Andrija Ričko said...

Hi
Can you please make code for MPU6050 modul to get accelerometer, gyroscope and temperature values.

Thank you

Tracker J said...

Unfortunately I don't have a MPU6050 module.
Are you talking about something like this one? http://www.ebay.com/itm/171907295226

Andrija Ričko said...

Yes. Thats the one, and I can't make it to work.

Tracker J said...

Cannot find anybody with one MPU6050 around, ordered one from Ebay, but it will take some time ...

Andrija Ričko said...

Okay, If you get it working please let me know.
Thank you.

Tracker J said...

I have received the MPU6050 Modules. I will post a related article this days, start it already. It's working OK.

Tracker J said...

And the related MPU6050 Article : http://www.esp8266-projects.com/2015/12/mailbag-mpu6050-module-i2c-driver-init.html

Zech Dz said...

Do you have any experience using the MLX90614. I've been trying it using the same procedure you documented here but it's just not working out. From what I've found it should work with normal I2C...

Tracker J said...

I don't have such a module but if is for interest for many of you, I can order some. What's more exactly behind the "not working"? is it not responding at all or just cannot obtain the right values from it?

Zech Dz said...

I just get 255 on all reads which I'm not even sure is right because when I test scanning for I2C devices for all device names on all pins my device doesn't even show up. I thought it showed up once but I can't get to that again. I also purchased 5, and all 5 of them cannot be found....

I purchased the breakout board so it has all the capacitors and resistors on it already, so I don't think I need any more pull-ups

Zech Dz said...

I think its "not responding at all" rather than just returning bad values.

Tracker J said...

255 looks like as you said, no response at all. I will suggest you to check your I2C bus connections and also might be a good idea to take a look at this tutorial, that is explaining a lot about some I2C init errors: http://www.esp8266-projects.com/2015/12/mailbag-mpu6050-module-i2c-driver-init.html.

AND

also check your MLX90614 IC version! From datasheet it looks like they are 2 available models, 3.3V and 5V!

I'm not able to find any MLX90614 close around but will do my best to grab some :)

If anybody want to donate modules, devices, etc, for evaluations and tests requests, please feel free to drop a line.

andres said...

Hi, can you explain me, how i should obtain the values of dev_addr and reg_addr. I´m working for first time with SparkFun Analog to Digital Stereo Converter Breakout - PCM1803A to sample audio signals and to send to another NodeMCU ESP8266.

First, i have done this:

id = 0 -- Numero del Bus
sda = 1
gpio.mode(sda,gpio.OUTPUT)
scl = 2
gpio.mode(scl,gpio.OUTPUT)
-- Inicializa i2c, configura pin1 como sda, y el pin2 como scl
i2c.setup(id, sda, scl, i2c.SLOW)

-- user defined function: read from reg_addr content of dev_addr
function read_reg(dev_addr, reg_addr)
i2c.start(id)
i2c.address(id, dev_addr, i2c.TRANSMITTER)
i2c.write(id, reg_addr)
i2c.stop(id)
i2c.start(id)
i2c.address(id, dev_addr, i2c.RECEIVER)
c = i2c.read(id, 3)
i2c.stop(id)
return c
end

-- get content of register 0xAA of device 0x77
reg = read_reg(0x77, 0xAA) --read_reg(dev_addr, reg_addr)
--print(string.byte(reg))


How, I know the right values for this device, and how i can see the samples in real time.

Thanks for share

Tracker J said...

Hi Andres,
I don't have such a board or chip around, but I think you are looking in the wrong direction. From the datasheet it looks like that PCM1803A ADC is using a I2S ( inter-IC SOUND bus) – a serial link especially for digital audio interface, not I2C. And it makes more sense to be so, as high speed audio sampling can be done on I2S interface.

Andres_Hita said...

Thanks Tracker J, I was doing a grand mistake. Now i have to redo how to conect these devices or definetly change it.
Do you know some device that i can use for this objetive? I need to sample audio signals but the maximum sample rate that has the ESP8266 is not enough. First i used MCP3201 (ADC with SPI) But it does not work. I could not make the connection between them. Now, i´m going to research how do it in a diferent way, so i hear Tips.

The general idea is to then send the audio data over Wi - Fi using a UDP protocol. I already did , but I fail even sampling

Thanks for share

Andres

Axit Vekariya said...

Can anyone explain what this following script does.
I can not understand how it identifie dev_addr,reg_addr. are they variables?

read_reg = function(self, dev_addr, reg_addr)
i2c.start(self.id)
i2c.address(self.id, dev_addr ,i2c.TRANSMITTER)
i2c.write(self.id,reg_addr)
i2c.stop(self.id)
i2c.start(self.id)
i2c.address(self.id, dev_addr,i2c.RECEIVER)
c=i2c.read(self.id,2)
i2c.stop(self.id)
return c
end

Axit Vekariya said...

I have nfc board(PN532) which I want to interface with ESP8266-12e board through I2C communication. can anybody help me out with the code. any similer example will be helpful to make it happen.

Tracker J said...

Hi Axit,

Yes, they are variables and are defined above as needed.

Regarding the NFC Board I will be more than happy to help you with a driver for it but I don't have any around. If somebody can provide one and a decent datasheet will will give it a try :)

Happy breadboarding,
TJ.

Post a Comment