Using a MAX6675 temperature sensor without a library

When you are using a sensor, or other type of peripheral, it is always handy to use one of the available libraries. Sometimes though there is no library, or the instructions for the peripheral are simple enough to not use a library.
In an earlier article, I showed how the (excellent) datasheet of the good old BMP180 offers a blueprint on using the sensor should you not have a library.

Purely as a learning moment, I will now do the same for an SPI sensor, the MAX6675 temperature sensor. yes I know, there is a library available, but consider this just as an excercise in dealing with SPI peripherals.
The MAX6675 datasheet states the following:

Force CS low and apply a clock signal at SCK to read the results at SO. Forcing CS low immediately stops any conversion process. Initiate a new conversion process by forcing CS high. Force CS low to output the first bit on the SO pin. A complete serial interface read requires 16 clock cycles.Read the 16 output bits on the falling edge of the clock.

The first bit, D15, is a dummy sign bit and is always zero. Bits D14–D3 contain the converted temperature in the order of MSB to LSB. Bit D2 is normally low and goes high when the thermocouple input is open. D1 is low to provide a device ID for the MAX6675 and bit D0 is three-state.

There is another piece of information hidden in the Datasheet, saying that the 12 Bit output is in steps of 0.25 degrees.
Another piece of info that would come in handy, but that is void in the datasheet is the signal timings, so we have to just do some trial and error.

In the table of the SO output register we see that apart from MSB (bit15) we also do not really need the 3 LSB’s, but we will read them anyway and deal with them. Bit 2 does have some function as it indicates whether the Sensor is connected or not.
Finally, we have to multiply the 12 bit value we found with 0.25 because  the raw value found gives the number of quarter degrees.

So in order to get the temperature we need to do the following things:

  • Begin with CS low to stop any conversion. To start fresh so to say
  • Then make CS high, to start the conversion
  • Pull CS low to output the bits to MISO on falling edge of clock.
  • Cycle the clock to read all the 16 incoming bits on the MISO pin
  • Ignore the MSB (bit 15)
  • Discard the 3 LSB’s by a right shift 3
  • Multiply the found value with 0.25 (or divide by four).

In Software that goes like this:
Define the pins and variables we will use

#define cs D8 //cs
#define clk D5 //sck
#define miso D6 // so
//define variables
int v = 0;
float tempC;
float tempF;

In the setup, we define the pinModes and the begin states of the pins

void setup() {
Serial.begin(115200);
pinMode(cs, OUTPUT);
pinMode(clk, OUTPUT);
pinMode(miso, INPUT);
digitalWrite(cs, HIGH);
digitalWrite(clk, LOW);
}

In the loop, we  call a procedure that reads and returns the MISO bits

void loop() {
v = spiRead();
if (v == -1) {
  Serial.print("Sensor not found");
}
else {
  tempC = v * 0.25; //Celsius
  tempF= tempC * 9.0/5.0 + 32;//Fahrenheit
  Serial.print(tempC);
}
delay(500);
}

Then in the spiRead procedure, we read the  the first MSB (bit15) and discard it.  Then in a loop we read the remaining 15 bits (bit 0-bit 14) and add them to a variable, we then discard bit 0,1 and 2, so we have a 12 bit number left. This number than still needs to be multiplied by 0.25 to give the result in degrees celsius

int spiRead() 
{
int value = 0;
digitalWrite(cs,LOW);
delay(2);
digitalWrite(cs,HIGH);
delay(200);

//Deal with bit 15 first
digitalWrite(cs,LOW);//Pull CS low to start conversion
digitalWrite(clk,HIGH);
delay(1);
digitalWrite(clk,LOW);// bit is read on downflank

//Read bits 14-0 from MAX6675 and store in 'rawTmp'
for (int i=14; i>=0; i--) 
{
  digitalWrite(clk,HIGH);
  rawTmp += digitalRead(miso) << i;
  digitalWrite(clk,LOW);
}
// if bit D2 is HIGH no sensor connected
  if ((rawTmp & 0x04) == 0x04) return -1;

//remaining 14 bits read, now discard bits 0,1 and 2
//we do that with a shift right three places
return rawTmp >> 3;
}

As we have to multiply the rawTmp value with 0.25, or divide it by 4. You might be enticed to do that with  a bitshift because after all, division by 4 is a rightshift with 2.
So, in theory it is possible to combine:
v=rawTmp >>3
tempC=v*0.25
by v=rawTmp >>5
Yet that is not a good idea.
Suppose we find the value “101001000” (=3 28)
with a rightshift 3 that becomes “101001” (=41)
divided by 4 that is 10.25 degrees.
However, if we would have combined it in a rightshift 5, we would have gotten “1010” which is 10.
Download the code here.
In a next article, I’ll hook up the MAX6675 to an ESP8266-01 and will make a monitoring system for a fireplace or cooktop

A cheap 1 channel relaymodule for ESP8266-01. Modifications revisited

In an earlier post, I discussed 2 popular 1 channel ESP8266-01 relay boards that are widely available on  ebay and chinese webstores. One of these boards -the simplest- seems to cause a lot of problems with the unsuspecting buyer, as it doesn’t work.
So, it might be good to address the problems around this board again, in a more practical session.

Be aware that this circuit is intended for the ESP8266-01S, but it can be made to work with the regular ESP8266-01.

The main reason why this board  does not work, is because the designer of  the PCB was drunk, or high on drugs when he designed it, or maybe he just didn’t understand the workings of the ESP8266-01.
His failure becomes immediately clear when we look at the circuit:
The designer used GPIO0 to trigger the relay pin.
There is nothing wrong with that, but as GPIO0 needs to be pulled high to let the ESP8266 start in UART mode (i.e. a normal start). The consequence is that the relay is activated on start-up. “No problem” must the designer have thought, “I just add a 2k resistor to pull it down”. Sure, that works, but the ESP8266 then just will not boot anymore.
So, we need to remove that 2k resistor that is indicated in the circuit above. On your PCB it is indicated as R2 and labelled with ‘202’.

A simple nudge with a hot soldering iron suffices, but make sure you remove the right one.

The next issue is that there is no connection between the Chipselect (EN or CH_PD) and the Vcc. So that needs to be fixed. Ideally that can be done with a 10k resistor, but it is most practical to just make the wire bridge as indicated in the circuit above, like this:

If you have an ESP8266-01S, then you are set, the board should work.

If you have a classic 512kRAM ESP8266-01, then you need to do one more thing: you need to add 10k pullup resistors between GPIO0 and 3V3 and between GPIO2 and 3V3.
When you have done this, the module will also work with an ESP8266-01.

Now  you have your 01 or 01S working  but, the relay will be activated on startup. That can be handled in several ways:
Add a “digitalWrite(0,HIGH);” instruction to the beginning of your program….or….
Use the NC contact to make your connection….or….
reroute the track to pin 0, to another pin (GPIO 1 or GPIO3) and give that pin a pulldown resistor.

If you want to control the board via MQTT, this program might be of interest to you. If you want to use a webpage on your phone, a simple Webserver will do.

Just one more thing about removing the 2k resistor. You may have come across people that do not mention removing the 2k resistor. They solder a 3k3 resistor between 3V3 and GPIO0. Well, yes that works. Why? Well effectively they make the pull up resistor on GPIO0  about 2k5 (the 10k in combo with the 3k3). So that gives a voltage divider of 2k5 over 2k, which makes 1.46 Volt out of  3.3V, which is high enough to be seen as HIGH by gpio0. That is indeed a less elegant method, but it works.

Now if you happen to have the  other relay board, the one with the STC15F104-SOP8 micro controller in addition to the ESP8266, and you dont get that working, begin with checking if the CH_PD/ EN pin is indeed pulled up to 3V3.

MQTT -HTTP-OTA program template

I provide a template that can be used as a start for your own MQTT program on an ESP8266. It connects either through DHCP or via a static LAN IP.
It implements basic MQTT functions (subscribe and publish) and I have added a hoist of functions/MQTT messages that you might find useful.
For instance:

  • IPnr
  • MACnr
  • ChipId
  • Uptime
  • Heartbeat
  • TimeDate (internet), including the ‘Day of Year’
  • RSSI
  • Filename
  • A number of memory and core functions
  • HTTP-OTA Download here.

The WebOTA is quite basic, upon sending an update command via MQTT, the program seeks a bin file on a server that you need to specify. You can get a bin file from your program by pressing Alt-Ctrl-S in the Arduino-IDE.

The name the OTA expects -and what is generated by Alt-Ctrl-S – is based on the filename plus the board, followed by ‘bin’. So in this case that is “Template.ino.generic.bin”. If you compile it for a Wemos D1, that file will become “Template.ino.d1_mini.bin”

Sending ESP8266 sensordata to GoogleSheet

If you want to store sensor data coming from your ESP8266 (or ESP32) project ‘in the cloud’, then there is a hoist of options. One option is what most people will already have available, namely GoogleSheet.
There are a number of tutorials on how to set that up, and i will just describe how I did it here, as part of a larger project I will publish later.

What we will do is:

  • Set up a new Google Sheet
  • Add a script to that sheet that places data in the sheet
  • Make a function for the ESP8266 that calls that script and hands it the data we want stored.

During the course of creating the GoogleSheet and the GoogleScript, we will need to take note of two numbers: The “sheet ID” and the “script ID”. The latter is also referred to as the GAS_ID
The GAS_ID usually slooks something like: “AKfycbxp……………………”
We will put the GAS_ID in the ESP code calling the script we created.
We will put the sheet ID in the GoogleScript, so the script knows in what sheet to place it.

Creating the Google sheet and acquiring the sheet ID
Go to your google drive on “drive.google.com”
Select New-GoogleSheets
A new sheet will becreated, give it a meaningful name. If you then look at the URL that will look something like:
https://docs.google.com/spreadsheets/d/1UML3C_P1H…&#8230;..hqCxos/edit#gid=0
The part between “/d/” and “/edit$gid-0” is the sheet ID. Copy that somewhere.

Creating the Google script and acquiring the GAS_ID
In your new GoogleSheet, go to “Tools-Script Editor”
Give the script the same name as you gave your Sheet

Paste the following code:

function doGet(e) { 
  Logger.log( JSON.stringify(e) );  // view parameters
  var result = 'Ok'; // assume success
  if (e.parameter == 'undefined') {
    result = 'No Parameters';
  }
  else {
    var sheet_id = '197ZGbUbbFmKtV-TPEtxYCXVRxt7YB00HQukSo-QwX14'; 		// Spreadsheet ID
    var sheet = SpreadsheetApp.openById(sheet_id).getActiveSheet();		// get Active sheet //getSheetByName('naam') voor ander blad
    var newRow = sheet.getLastRow() + 1;						
    var rowData = [];
    var sheet2=SpreadsheetApp.openById(sheet_id).getSheetByName('Sheet2');
    var newRow2=sheet2.getLastRow() + 1;
    var rowData2 = [];
    rowData[0] = new Date();    // Timestamp in column A
    rowData2[0] = new Date();
    for (var param in e.parameter) {
      Logger.log('In for loop, param=' + param);
      var value = stripQuotes(e.parameter[param]);
      Logger.log(param + ':' + e.parameter[param]);
      switch (param) {
        case 'temperature': //Parameter
          rowData[1] = value; //Value in column B
          result = 'Written on Column B';
          rowData2[1]= value;
          break;
        case 'humidity': //Parameter
          rowData[2] = value; //Value in column C
          result += ', Written on column 3';
          break;
        case 'moisture' :
          rowData[3]=value;
          result += ', Written in Column D';
          break;
        default:
          result = "unsupported parameter";
      }
    }
    Logger.log(JSON.stringify(rowData));
    // Write new row below
    var newRange = sheet.getRange(newRow, 1, 1, rowData.length);
    var newRange2= sheet2.getRange(newRow2,1,1, rowData.length)
    newRange.setValues([rowData]);
    newRange2.setValues([rowData]);
  }
  // Return result of operation
  return ContentService.createTextOutput(result);
}
/**
* Remove leading and trailing single or double quotes
*/
function stripQuotes( value ) {
  return value.replace(/^["']|['"]$/g, "");
}

Change the spreadsheet ID into your spreadsheet ID

Next go to Publish-Deploy as Web App

Change the acces type to ‘anyone, even anonymous’ and deploy

After deployment, you will be shown a url. Copy that as it contains the GAS_ID


The URL will look like https://script.google.com/macros/s/AKfycbxPrTJNtUgXxqMX&#8230;
The Script ID  (GAS_ID) is AKfycbxPrTJNtUgXxqMX…
This is the ID we will use in the ESP8266 code.
Before you leave the Google Sheet, go back to the Sheet itself and make sure there is a sheet1 and a sheet2.

This is not an absolute necessity, but it allows me to show how you can write to different sheets in your main sheet. In your neck of the woods those might be called “Sheet1” and “Sheet2”

The ESP8266 code

String GAS_ID = "";//getactivespreadsheetID
const char* fingerprint = "46 B2 C3 44 9C 59 09 8B 01 B6 F8 BD 4C FB 00 74 91 2F EF F6";
const char* host = "script.google.com";
const int httpsPort = 443;

void sendData(int x, int y, byte z)
{
  Serial.print("connecting to ");
  Serial.println(host);
  gsclient.setInsecure();
  if (!gsclient.connect("script.google.com", httpsPort)) {
    Serial.println("connection failed");
    return;
  }

  if (gsclient.verify(fingerprint, host)) {
    Serial.println("certificate matches");
  } else {
    Serial.println("certificate doesn't match");
  }
  String string_x     =  String(x, DEC);
  String string_y     =  String(y, DEC);
  String string_z     =  String(z, DEC);
Serial.println(string_x);
Serial.println(string_y);
Serial.println(string_z);
  String url = "/macros/s/" + GAS_ID + "/exec?temperature=" + string_x + "&humidity=" + string_y + "&moisture=" + string_z;
  Serial.print("requesting URL: ");
  Serial.println(url);

  gsclient.print(String("GET ") + url + " HTTP/1.1\r\n" +
                 "Host: " + host + "\r\n" +
                 "User-Agent: BuildFailureDetectorESP8266\r\n" +
                 "Connection: close\r\n\r\n");

  Serial.println("request sent");

  while (gsclient.connected()) {
    String line = gsclient.readStringUntil('\n');
    if (line == "\r") {
      Serial.println("headers received");
      break;
    }
  }
  String line = gsclient.readStringUntil('\n');
  Serial.println(line);
  if (line.startsWith("{\"state\":\"success\"")) {
    Serial.println("esp8266/Arduino CI successfull!");
  } else {
    Serial.println("esp8266/Arduino CI has failed");
  }
  Serial.println("reply was:");
  Serial.println("closing connection");
}

This function takes 3 arguments that will be placed in  the Google sheet.
Back to the Script

The Lines:

var sheet_id = '1DdQ-dc6bpsP7EtvtvBHvl-cZai1qUC6QfNtaxTnh6w0'; 	
var sheet = SpreadsheetApp.openById(sheet_id).getActiveSheet();

go to the proper sheet and open the sheet that is active.
The lines:

var newRow = sheet.getLastRow() + 1;						
    var rowData = [];
    var sheet2=SpreadsheetApp.openById(sheet_id).getSheetByName('Sheet2');
    var newRow2=sheet2.getLastRow() + 1;
    var rowData2 = [];

Create a new Row in the first sheet and create a new Row in the second sheet. Beware ‘Sheet2’ is the NAME ofyor second sheet. if you have given it a different name, that shoud be reflected here
The lines:

rowData[0] = new Date();    // Timestamp in column A
rowData2[0] = new Date(); //Timestamp Sheet2 column A

insert a timestamp in the first column of both sheets
The lines:

for (var param in e.parameter) {
      Logger.log('In for loop, param=' + param);
      var value = stripQuotes(e.parameter[param]);
      Logger.log(param + ':' + e.parameter[param]);
      switch (param) {
        case 'temperature': //Parameter
          rowData[1] = value; //Value in column B
          result = 'Written on Column B';
          rowData2[1]= value;
          break;
        case 'humidity': //Parameter
          rowData[2] = value; //Value in column C
          result += ', Written on column 3';
          break;
        case 'moisture' :
          rowData[3]=value;
          result += ', Written in Column D';
          break;
        default:
          result = "unsupported parameter";
      }

insert the 3 parameters that are received in the first sheet and the second sheet. The sole reason I create two sheets that get the same data is to demonstrate how to use two sheets.
The example is only logging 3 values, but when looking at the code it should be quite obvious on how to add more data.
One warning: when you edit the script, the previous versions remain, when done, you need to select the right version of the script to use:

An ESP8266 with Static IP, sending DHT11 data via MQTT, going into DeepSleep

Sometimes, all you need is a quick program that needs to send some sensor data from a location that you don’t want or can’t put a power cable to.
Battery power, sending an ESP8266 into deepsleep for most of the time can be the answer.

The program here does just that. For reasons of speed it uses a static IP set up.
Dont forget to connect GPIO16 to the RST pin on your ESP8266.  A bare ESP8266 is your best choice. If you are using an ESP8266-01, you will need to have good eyes to solder a wire between pin 16 and the RST pin

You can download the full program here.

MX1508 vs L9110S vs TB6612 vs L293 Motordriver board

Two popular, cheap motordriver boards that are available on chinese webstores are the MX1508 and L9110S.

MX1508
At Aliexpress and Banggood, the MX1508 is listed as a “Dual Channel L298N DC Motor Driver Board PWM Speed Dual H Bridge Stepper Module”.However it is an MX1508.

This board can drive two motors independently. It is an H-bridge configuration for each motor and so can drive the motors in either direction. It is rated at 1.5A on each motor with a peak of 2.5A.
The module itself is not really breadboard friendly. The pin spacing is 0.1″ but the various connectors are at aberrant spacing.
The MX1508 from Shenzhen Guanghui Electronics Co., Ltd.  has a working voltage of  1.8-5Volt, but can drive motors with an operating voltage of 2-9.6 Volt. The module however, has only 1 Voltage input, that supposedly can take 2-10 Volt. The digital part of the MX1508 however is fed through a 220 ohm resistor with the resulting voltage being capped by what I presume to be a zenerdiode of 5 Volt.  With most hobby motors being either 6 or 12 Volt, 10 Volt may not be the most practical voltage.
When analyzing the PCB, one odd thing becomes clear: Pin 4 (VDD1), the input for the operating voltage of Motor A, seems to be not connected to anything. Yet the module works. Connecting it seems the right way to go though. It is very well possible that it is an unintentional mistake in the PCB.

The MX1508 has no fly back/free-wheeling diodes present, though they are advised by the chip manufacturer. As a result, there might be big spikes on the powerline when you are reversing the direction, which could lead to problems with other devices connected. Connect the motor like in the figure to the right. Controlling a DC motor with an H-bridge such as the MX1508 is in fact quite simple. Yet there is a library available.

The only  (currently) available datasheet is in chinese. It advises to put a 100nF over each motor output, something that is not done on the module PCB.

There is also an MX1208 with a current of 1.3A (2A peak). These are only available as a bare chip, not as a module.

Although the chinese webstores market the MX1508 as a replacement for the L298N motordriver, that is with its 46 Volts and 4 amp total, quite a strech.

The L9110S
So, if 10 Volt motorvoltage is not enough, there is the L9119S from Asic. The available module has in fact two L9110 chips, as they each control only one motor. The module can control 2 motors that according to the datasheet can be 2.5-12V with a maximal continuous current of 800mA , with a peak of 1.5~2 Amp.
The labeling on the input pins can be a bit confusing, but it is like this:

  1. B-IA: Motor B Input A
  2. B-IB: Motor B Input B
  3. GND: ground
  4. VCC: 2.5V-12V DC
  5. A-IA: Motor A Input A
  6. A-IB: Motor B Input B

The L9110 chips each have 2Vcc and Ground pins, but there is no indication that these should get different voltages (like one for the chip and one for the motor) and on the module PCB all grounds are connected and all Vcc’s are connected. There is however a small point of concern in using the module with a non-TTL level like 12 Volt: The inputs are pulled up via 10k resistors to the Vcc. That means that if you use 12 Volt DC for the motor and a 3v3 microprocessor to control the motor, on a LOW level there will be possibly flowing 1.2 mA into the uC pin. That does not need to be a major problem, but it is something that needs to be taken into account. If it is a problem, a simple optocoupler circuit can be put in between. Though motors are easy to control with an H-bridge such as this one, there are several libraries for the L9110S.

TB6612
The TB6612 from Toshiba is another relatively cheap board available at chinese webstores. It can drive 2 DC motors (or one stepper) with 1.2A per channel (3A peak). It runs at 2.7V-5V logic (2.7-5.5V according to some sources). The motor voltage is separate from the 2.7-5.5 Volt logic voltage. Good for motor voltages from 4.5V up to  15V (though the max supply  voltage is rated at 13.5V).   It comes with built in kick-back diodes internally.
The control of the TB6612 is a bit different compared to other motor drivers. Where usually the speed of a motor in an H-bridge is controlled by a PWM signal on one of the 2 control pins, the TB6612 has a seperate PWM pin, next to the 2 control pins. It can be pulled up if not used. It also has a standby pin to rapidly disable both motors. Connect to ground to disable. On the ‘Chinese’ boards, it is usually left floating.
Though DC motors are easy to control with an H-bridge such as this one, there is a library for the TB6612.

Though these modules are clearly no competition for the L298, how do they compare to the L293?

MX1508 L9110 TB6612 L293
Voltage 2-10V 2.5-12V 4.5-15 4.5-36V
Current 1.5A 800mA 1.2A 1A (0.6 for 293D)
PeakCurrent 2.5A 1.5-2A 3A 2A (1.2 for 293D)

LSC smart sensors, switches and lamps

Dutch thrift store ‘Action’ had a promotion of TuyaSmart compatible IoT devices for quite low prices. The devices were:

  • Smart switches
  • Smart doorsensor
  • Smart movement sensor
  • Smart alarm
  • Smart lamps and led strips
  • Remote control

I found the efficiency (as in lumen per Watt) of the lamps a bit low, with exception of one 800 lumen lamp, and the ceiling lamp was not really my taste so I didnt bother with them. The remote control seemed somehow obscure, as it seemed it could only control one device. The other devices though were quite promising so I bought those.

Though LSC has it’s own app, I already had the TuyaSmart app, so I decided to use that one. The LSC app is a spitting image of the Tuya app, but it is not recognized by e.g. Google Home, whereas Tuya is.

The EU type switches are quite compact, but you may still ha e trouble putting 2 right next to each other in a double outlet. The setup is very easy and if you use the ‘scan’ option in the tuya app, not much can go wrong.

The door sensor and the alarm are also easy to set up. The doorsensor is coincell operated and the two parts can be stuck to the door and door frame. In the Tuya app, actions can be attached to the doorsensor to e.g. switch on a light, or sound an alarm. The sensor sends notifications to your phone as well.

That leaves the motion sensor and somehow that was a different story. Connecting to wifi was simple, but after that, nothing g happened. No amount of movement seemed to be noticed. As another sensor had the same behaviour, I had not much choice but to return them.

Yet…..I kept wondering. Just couldn’t imagine two sensors were not working, especially as no one else seemed to return them while they were selling like hot cakes.

So….I bought another one…..same result. Various reinstalls, even in the LSC app, showed no effect, so I left it, frustrated, deciding to return it after the weekend.

And then….out of the blue, it started to work, and quite well I should add. The sensor does ha e a bit of a refractory period, meaning that after it senses movement and sends a notification, it will not sense new movement for a few minutes. People have reported this to be about 5 min, but in my experience it is more like 3 minutes and occasionally even 1 minute.

TuyaSmart

The app seems to have one slight shortcoming though: When a movement is registered and shown at the bottom, the app only shows  “intruder” for a short time and goes back to its ‘No intruder’ status quickly. But that is only a cosmetic short coming.Commercial software or Flashing?

The Tuya app, working with the software in the devices does what it does and it is quite good software. Although I do not believe that ‘the chinese’ are particularly interested in when I switch my lights on, there are many good reasons to put your own software on an IoT device: Not being dependent on 3rd party servers that might go out of business, availability on LAN rather than only WAN, no deluge of different (possibly shady) apps on your phone.
The devices I had all appeared to have an ESP8266 under the hood and could be opened.

LSC motion sensor