Friday, August 28, 2015

ESP8266 - NodeMCU - I2C - Programming considerations







The story behind:

  Many people are complaining about I2C communications problems, mostly related with the NodeMCU LUA.

  I found out that many of the related problems are generated from a easy to fall in trap, generated by the way how code is executed in case of a step-by-step debug run or using a interpreter console, as in our above NodeMCU LUA case.

   Let's take a very simple example code and with the help of a Oscilloscope let's see what's happening exactly. I have choosen a LM75 I2C temperature sensor for our example, as been widely used and also people heavily complained about communication problems with. For more details about the code take a look at the LM75 Temperature sensor Article.

address = 0x48  -- LM75 I2C Address
temp_reg = 0     -- temp reg

bus = 0              -- I2C bus
sda, scl = 2, 1   -- Used pins for SDA and SCL
 

-- init I2C Bus  
   i2c.setup(bus, sda, scl, i2c.SLOW)
 

-- LM 75 Communication
  i2c.start(bus)     
  i2c.address(bus, address, i2c.TRANSMITTER)
  i2c.write(bus, temp_reg)
  i2c.stop(bus)


  i2c.start(bus)
  i2c.address(bus, address, i2c.RECEIVER)
  c=i2c.read(bus, 2)
  i2c.stop(bus)

  h,l = string.byte(c,1,2)
  if h > 127 then h = h - 255 end       
  if l > 127 then l = 5 else l = 0 end  
  temp=string.format("%d.%d", h,l)
  print(temp)


If you take the code from above, save it on ESP8266 module as lm75_test.lua and run it after that as
dofile("lm75_test.lua")  you will see that the result is OK, typing the right temperature, 29C in our case.

LM75 - I2C Communication - 500 us timebase

  If we zoom in a bit, we will be able to identify also our code snippets:
  
   i2c.start(bus)     
  i2c.address(bus, address, i2c.TRANSMITTER)
  i2c.write(bus, temp_reg)
  i2c.stop(bus)


-> address    = 0x48 
-> temp_reg = 0x00 

LM75 - I2C Communication - zoom in - 1


  i2c.start(bus)
  i2c.address(bus, address, i2c.RECEIVER)
  c=i2c.read(bus, 2)
  i2c.stop(bus)

  h,l = string.byte(c,1,2)

 - > h = 29 (0x1D)  
 - >  l = 0 (0x00)

LM75 - I2C Communication - zoom in - 2

 So, as you can see above, all OK and confirmed also by decoding the I2C communication on the Oscilloscope.

Now, let's see what's happening when running the same code  from the console (just select the code and press "Execute Selection" button.

-> h = 255
-> l = 255
-> temp = 0.5

WHAT?! WHY?!

Let's take a look on the Osciloscope also for this Run, using the same timebase, 500us :

LM 75 - I2C Communication - Run from console - 500us



Confused?  Let's increase the time base to 500ms and let's see what we can found about:

LM 75 - I2C Communication - Run from console - 500ms

What is telling us the last picture? The TIMING now is totally wrong !!

 Why?
 Because of the way that the code is sended to the interpreter and the way that is run, line by line!!

And please remember , these nasty things might happen in some conditions, even in case of a compiled code, not interpreted, when the code is in a step-by-step debugger mode run!

Might look silly and trivial, but imagine a way more complex code, all working OK, adding some extra modules, start debugging for them and suddenly starting having problems with previous tested modules, some nice working ones!
If you fall in such a trap, you can end up to spin around in circles for weeks.

What can be the solution? Easy. Just use the Rule number one: keep sensitive things like communication/timing and stuff at "atomic" level! Reading carefully the Datasheets can help you to quickly identify the "atomic granulation".

How can be done that in a easy way? Simple. Just use a FUNCTION!

Rememer, using functions in you code will give you not only the advantage of high readability, a modular and structural programming, easy debugging, etc,  but also, if functions are designed properly, can help you to avoid a potential pitfall as the one above.

Corrected code:

address = 0x48  -- LM75 I2C Address
temp_reg = 0     -- temp reg

bus = 0              -- I2C bus
sda, scl = 2, 1   -- Used pins for SDA and SCL
 

-- init I2C Bus  
   i2c.setup(bus, sda, scl, i2c.SLOW)
 

-- LM 75 Communication
function read_temp()
  i2c.start(bus)     
  i2c.address(bus, address, i2c.TRANSMITTER)
  i2c.write(bus, temp_reg)
  i2c.stop(bus)


  i2c.start(bus)
  i2c.address(bus, address, i2c.RECEIVER)
  c=i2c.read(bus, 2)
  i2c.stop(bus)

  h,l = string.byte(c,1,2)
  if h > 127 then h = h - 255 end       
  if l > 127 then l = 5 else l = 0 end  
  temp=string.format("%d.%d", h,l)
  print(temp)

end

read_temp()

And Yes, you can refine it even better than that :)

function init_I2C(sda, scl)
    i2c.setup(bus, sda, scl, i2c.SLOW)
end

function read_temp()
  i2c.start(bus)     
  i2c.address(bus, address, i2c.TRANSMITTER)
  i2c.write(bus, temp_reg)
  i2c.stop(bus)


  i2c.start(bus)
  i2c.address(bus, address, i2c.RECEIVER)
  c=i2c.read(bus, 2)
  i2c.stop(bus)
end 


function print_temp(c)
  h,l = string.byte(c,1,2)
  if h > 127 then h = h - 255 end       
  if l > 127 then l = 5 else l = 0 end  
  temp=string.format("%d.%d", h,l)
  print(temp)

end

-->>
init_I2C(sda, scl)
read_temp()
 print_temp(c)
 29.0






Thursday, August 20, 2015

Mailbag - ESP8266 Weather Station 433Mhz KN-WS400 hack




  Using a 433Mhz radio link the ESP8266 CBDB Dev Board module equipped with a DHT22 temperature/humidity sensor is uploading data directly to the Konig 433 mhz wireless station with hygro-thermometer [KN-WS400N] :





And the story behind:


As been working to a ESP DHT22 sensors array for a Environment data logging system I was thinking that it would be nice to have also a direct reading panel for the data. And because just received in my mailbox few days ago a KN-WS400 Weather station to be used for another project I said why do not to take a deeper look and see if we can use it also as a realtime data view panel for our DHT22 sensors.

As you can see from the picture below, it has a big and nice looking display (not the best quality or contrast in town but, hey, it's just a 40USD unit, nothing fancy):

KN-WS400 Weather station

   It has only temperature and humidity capabilities, but for our project is more than enough:).


     What is interesting about KN-WS400 is the fact that it can receive temperature and humidity data also from another 3 external sensors (but delivered in the box with only one!).
You can setup and choose between 3 different channels, each one allocated for a separate remote sensor unit.


Remote sensor


Reading remote temperature data on Channel 1


Channel setup is done very simple, choose your desired free channel from your remote sensor unit and reset the corresponding channel on the Weather station by pressing more than 10 sec the channel button.

External sensor CHannel setup and C/F scale option buttons


    So far so good. The idea is to try to use another 2 ESP DHT22 sensor units to send the temperature and humidity data directly to WS400 Weather station on the available channels. Or, why not, to replace them all 3 with the WIFI Web enabled ESP DHT22 smarter ones :)

   To do this, first of all we need to take a look at the transmission frequency to know what kind of receiver/transmitter to use with our ESP DHT22 Modules.
   As is written on the back of the unit, the KN-WS400 Weather station use for remote sensors communication the standard 433Mhz frequency, same as probably most of the zillions different types of cheap weather stations available around.

So now, as we know the frequency, next we need to see if we can have any luck in decoding the communication protocol.



What do we need:

   I will not insist to much on the wiring setup as it is a very simple one, just connect your simple  433mhz Receiver module to your Power supply Vcc , GND  and the Data pin to your used Oscilloscope channel probe.

   As soon as it is connected and you start changing the channel on the KN-WS400 sensor module by pressing the CH button from the back of the unit, you will see a data burst on your Oscilloscope. See also Youtube video from above.

Data burst received on 433Mhz from KN-WS400 remote sensor

As you can see from the captured data above, it looks like the unit is transmitting the data in 6 separate data bursts. Actually, when zoomed in we can see that is sending the same data 6 times.






At a deeper look at the received data stream we can see that:
  •  Each transmission consists of 6 repetitions of the same data.
  • They are separated by a long sync signal ("preamble") that’s about 4.5ms (3.8ms low + 0.7ms high)
  • We can suppose the bit patterns (they might be inverted sometime 1<->0 but is not the case of this sensor) as:
           -   Logic 1 (or 0) is about 2.6ms -> 1.9ms low + 0.7ms high
           -   Logic 0 (or 1) is about 1.7ms -> 1.0ms low + 0.7ms high



"Preamble" measurement



Logic "1" Measuremnt



Logic "0" measurement


  Based on the assumptions from above, let's try to "transcribe" the signal from the above:

      PRE10101011100100010000010111110011010100000PRExxxxPRExxx4PRE

   Before even thinking to try to decode the data we need to be able to identify where reside each data value in the stream. For this taskthat can become quite complicated or impossible some time, you can start using a very simple technique: just change only one variable at a time, in very small increments if possible and look after changes.

  The easiest one to do, is the Channel setup, as we have on the back of the sensor unit a switch for it.

Press the channel button and change Channel to 1,2 and 3.
Capture and take a look at the received data :

PRE10101011100100010000010111110011010100000PRExxxxPRExxx4PRE
PRE10101011101000010000010111110011010100000PRExxxxPRExxx4PRE
PRE10101011100000010000010111110011010100000PRExxxxPRExxx4PRE  

Ahaaa..have you seen it? :)

 Let's arrange the data in a more readable format: 

1010 1011 1001 0001 0000 0101 1111 0011 0101 0000
1010 1011 1010 0001 0000 0101 1111 0011 0101 0000
1010 1011 1000 0001 0000 0101 1111 0011 0101 0000

  Looking better? :)

  Now you can clearly see that the channel data (CH) is encoded on 2 bits in the 3rd column. Not bad at all. You know why? because despite the fact that you cannot find 2 Weather stations with the same exact data format they are quite similar and any existing information can help you a lot in the reduction of the complexity of the decoding process. 
 
  Let's make another assumption based on the info that we already have from other existing models, that the first 2 bits from column 3 to be the battery status ones. Usually they are coded in this way.

  Powering the sensor unit from an external lab PSU and reducing gradually the voltage until under normal working one the data obtained looks:

1010 1011 1001 0001 0000 0101 1111 0011 0101 0000
1010 1011 0101 0001 0000 0101 1111 0011 0101 0000
1010 1011 0001 0001 0000 0101 1111 0011 0101 0000

Where :

   "10" -> PWR GOOD
   "01" -> AVG
   "00" -> Replace battery


Nice. So, until now, we have identified the  Battery status and Channel ID.  Let's go further.


For temperature and humidity a common found coding technique is as follows:
  • Humidity is stored as low/high order nibbles. So you need to take the first 4 bits and tack them on the end of the next 4 bits. 11010101 becomes 01011101. 
  • Temperature is transmitted as 12 bits in 3x nibbles ranging from low to high nibbles. A fixed offset of 900 is applied to the temperature value so 0 degrees F = 900 and each degree F change is 10 decimal. Then the obtained value is divided by 10. And converted to degC. Confusing and painful stuff to find in a data stream.

Sounds complicated? Why are temperature and humidity data coded like that?
My personal opinion is that some specifications were created for the first designed one and now as these cheap weather stations has become such a mass product everybody is copying more or less the same design :)


If somebody really knows the true reason for such a coding, I will more than happy to know. Might be some good reasons behind that I'm missing. 


Let's see what's happening in our case. And before anything else, let's use the same technique, keeping all the data the same and change slowly Temperature only part:


Battery=2
Channel=2 
Humidity=29%

10101011 1001 0001 0011 1010 1111 0001 1101 0000 
10101011 1001 0001 0011 1001 1111 0001 1101 0000
10101011 1001 0001 0011 1000 1111 0001 1101 0000
10101011 1001 0001 0011 0111 1111 0001 1101 0000
10101011 1001 0001 0011 0101 1111 0001 1101 0000
10101011 1001 0001 0011 0100 1111 0001 1101 0000


Can you see the temperature data changing?

And you know what?
IT IS IN deg C already!!
If you don't believe me just convert the values in decimal and divide them by 10!

For this type of weather station is no more fancy Fahrenheit transformation voodoo thing. You can see it in clear above.


Then you know what? Let's try directly our luck also with Humidity values. We have a steady 29 above, let's find it:

10101011 1001 0001 0011 1010 1111 0001 1101 0000 
10101011 1001 0001 0011 1001 1111 0001 1101 0000
10101011 1001 0001 0011 1000 1111 0001 1101 0000
10101011 1001 0001 0011 0111 1111 0001 1101 0000
10101011 1001 0001 0011 0101 1111 0001 1101 0000
10101011 1001 0001 0011 0100 1111 0001 1101 0000


WOW! Now that was a nice surprise! All in clear, no more fancy calculations :)



So, what we have so far:

Unique ID   VBAT    CH     Temperature          ????       Humidity      0
10101011     10         01      0001 0011 1010     1111      0001 1101      0000


Puzzle is almost solved.

Only few things remained "undecoded" but believe me after capturing hundreds and hundreds of data streams it looks like de ID remains stable, same with the mysterious "1111" between Temperature and Humidity and the "0" from the end.

As it looks like there is no fancy Checksum algorithm involved or any special data stream identifier I think we can move further and design your own ESP DHT22 Module transmission program for direct data upload. And as you can see in the above Youtube video it was a success.



 
As soon as I cleaned it a little bit, the Part 2 will follow, with the full code description.


PS: I really hope that after reading this article the guys from Konig or who are doing the coding for this product will let it as it is because it has becoming the most desired cheap Wireless Weather Station around, believe me, they are selling like hotcakes :). Thank you Konig for such a nice surprise for all of us!

And to be clear: I am NOT afiliated in any way with Konig and the unit was not received from Konig. But if they want to thank me for opening a new market for their products I will not say no. lol.


Monday, August 17, 2015

Arduino IDE - WIFI MAINS Power Switch - ESP8266 MPSMv2

--------------------------------------------------------------------------------------------------------------------------
WARNING!! You will play with LIVE MAINS!! Deadly zone!! 
If you don't have any experience and are not qualified for working with MAINS power I will not ecourage you to play arround!
---------------------------------------------------------------------------------------------------------------------------- 
  
   For any new orders / requests please feel free to use as usual: tech at esp8266-projects.com.
 
   If you want for your own experiments to order ESP8266 WIFI Web Power Switch for MAINS - MPSM v.2 DevBoard bare PCBs only, you can also do it directly at Dirty PCBs, our preferred PCB House:


----------------------------------------------------------------------------------------------------------------------------



   For reference please take a look at the original WIFI MAINS power switch module (MPSMv2) article link:
http://www.esp8266-projects.com/2015/04/p2-wifi-web-power-switch-for-mains-mpsm.html



   As you might remember from the previous related article about MPSMv2 Board the software examples were done in NodeMCU LUA.

  I was asked to make also a short, basic introduction also for Arduino IDE, so here we go. But before of that I need to make also a short presentation of the MPSMv2 Board with few extra explanations. I will not insist too much on the hardware/schematics/theory of operation part of the story as you have all the details already in the previous articles about, including BOM.

  What I want to explain a bit is the concept behind that might help you to understand better how to use the board. To ease the understanding you can see MPSMV2 as 2 separate Boards spliced together,  a Breadboard friendly ESP8266 adapter with integrated 3.3V Power Supply, and a MAINS Triac Switch.

    The PCB is designed in such way that no harm will be done by cutting it in half as in the below picture.
MPSMv2 Board - Full and splitted

  You can use separatelly the 2 parts, the ESP8266 Adapter with 3V3 regulator and the Triac MAINS Power Switch.

MPSMv2 - ESP8266 adapter with voltage regulator & stuff on the back

   The Triac Switch part can be used directly even with a different ARM, PIC, Arduino, whatever MCU you have arround without any problems as long as it has a GPIO pin capable to drive the MOC Optocoupler LED thru CMD port.

Triac Driver part - use proper Fuse and EMI filtering!

   Please keep in mind that de design was done in such a basic way as been designed to be part of a bigger system and used for development and testing.

   Please use propper MAINS protection, FUSE and EMI filtering block based on your specific application requirements!

  There were a lot of questions and debates about, including the Hackaday one, why it has not a FUSE, where is the filtering, etc, etc ... they are not there because should not be there in our MPSMv2 design.
   Input protection/filtering block for MAINS should not be on the same board with the driver. They are a lot of explanations behind this but even only having more flexibility in mind should suffice.
If you look at the EMI filter below, similar with the choosen one as suitable to be used in our project you will understand even more why should be not on the MPSMv2 board.

EMI filter vs. MPSMv2 Board


    Now let's go back to our main story for today: how to use MPSMv2 with Arduino IDE.


What we will need:




1. Software installation

If you didn't have yet installed a proper running ESP8266 Arduino IDE environment the ESP8266 Arduino IDE installation Article might help you. Or the Youtube video from below:







2. Wiring

Connect the MPSMv2 Module to the USB adapter as in the picture below:

MPSMv2 - USB Adapter Wiring

USB         MPSMv2
  Rx      ->    Tx
  Tx      ->     Rx
  GND  ->    GND
  +5V   ->     Power input port +5V

   Set the PRG jumper (Blue) for programming mode ON !



3. Arduino IDE Programming.

  • Start Arduino IDE
  • Select inside IDE your desired ESP8266 Board (default one should be also OK).
  • Plug-in the USB adapter connected with the MPSMv2 module as above, will see the power on LED's 
  • Open in IDE the basic Blink program
  • Replace the  digital pin 13 as an output PIN with1.
  • Compile and upload the program to MPSMv3 board.


 The result:





Let's test also the Triac driver command pin:





Q & A :

  • How can you have more than a MAINS power switch channel available? Very simple: just cut the Triac driver part from other MPSMv2 PCB's and connect the Driver side only to the full size MPSMv2 Board. 
  • How many independent MAINS lines can be driven by a single MPSMv2 Board? As many as free IO pins you have available in your project and Triac Drivers you might add to it.   
  • What's the easiest way to have 2 MAINS lines swiched synchronous? Just connect the CMD port from a second available Triac module to the CMD port on the main MPSMv2 Triac Module. The Triac driver from the MPSMv2 will be commanded in sync with the second connected one.