Sunday, October 27, 2019

Work with DHT11 and 22 with Arduino without Using Library

Objective: 
Learn how to interact with both DHT11 and DHT22's, and how to extract its result from this device without using library in Arduino.
For example DHT11, pin arrangement are Vcc, Data, NA, GND
Proclaim: 
I choose a "hard" way to do this project just for learning a bit more about DHT11 and Arduino without using any external library.  If you looking for an effective way using DHT11, please do not use this example.  This example may confuse you if you do not fully understand what you are doing. 

Equipment: 

  • Nano Arduino
  • DHT11, DHT22
  • Oscilloscope

Schematic:
Standard setup with Arduino nano.  Connect 5V and GND between nano and DHT11.  On DHT11, connect its Vcc pin and data pine with 5k ohm resister.  Connect DHT11 data-pin to pin 2 on Arduino nano.  Oscilloscope measurement from nano pin 2.

DHT11 Data Structure:
  Its data pin need to be pulled down for >18ms by Arduino in order to trigger DHT11 to generate data.  After signal to be settled, DHT11 start sending 40 bits of data.  First 16 bit HR data, and next 16 bit temperature data, and the 8 bits of for Parity bit to verify data are received correctly.  See page 4 for detail.
Pull Down 20ms to trigger DHT11 to generated data.  The length of data is about 3ms.
Right after 20ms, it takes ~150us to be stabilized in my code.  The real data is starting from the third one.  The data we can see on screen is 00100

The first two pulses from Arduino are not data.  There are 40 bits data shown on this scope.  The length of data is ~3.7ms.  0 is about 28µs, and 1 is about 70µs.  Its data are 00100001 00000000 00010111 00000000 00111000.  HR data is the first two bytes.  Temperature are the 3rd and 4th byte.  The last byte is Parity byte. 
Above picture is copy from DHT11's datasheet.  See reference.



DHT11 Data Calculation Example:
  • Firs two byte are for humidity: 00100001 00000000 = 33%
  • The next two bytes are for temperature: 00010111 00000000 = 23°C
  • The last byte is parity byte: 00111000 = 56 (it is matched with 33 + 23).

Standard Final Test Result with "extract_data()"
When use "Display_Result()" with "Serial Plotter" in Arduino IDE.  This is a random result, not align with above result.
Reference:


Only first byte for HR, and third byte for temperature.
HR: 00100010 = 34, temperature : 00010100 = 20.  
Check sum: 00110110 = 00100010 + 00000000 + 00010100 + 00000000

Update on 10/27/2019, adding DHT22 calculation:
First and second byte for HR, and third and fourth byte for temperature.  Last byte for check sum.
HR: 00000001 01111001 = 377 =>/10>=>37.7%,
Temperature: 00000000 11100010=226=>/10=>22.6C, 
Check sum: 01011100 = 00000000+01111001+00000000+11100010
DHT22's data signal profile example.


From DHT22 datasheet page 3

Arduino code:
//DHT11 datapull by Arduino
//Author: Samson Yang
//Date: 10/26/2019
//It without using any libaray.  This is a very not effective way to pull data out of DHT11
//But, after familiar with how DHT11 and DHT22 work, the same experience can be applied to other device.
//read datasheet from
// http://www.produktinfo.conrad.com/datenblaetter/1400000-1499999/001405544-da-01-en-TEMP_UND_FEUCHTESENSOR_DHT11.pdf
//
int L=600;                        //Use 600 data point to pull data into matrix 
boolean data[1000];               //Need to be more then above L
boolean result[100];              //Need to be more then 40 
int resultcount=0;                //End result shoudl be 39.
int data_pin=2;                   //Set the data pin location
int extract_data_threadhold = 8;  //This threadhold between 0 or 1. It is the counts of high status between low voltage.
int DHT_type=22;                  //11 means DHT11, 22 means DHT22.  It will only be used at the end of data caculation

// Above setting is only good for Arduino nano.
// For faster board, you will need to change "extract_data_threadhold" and L.

void setup() {
  Serial.begin(9600);  
}

void Get_Data(){
  for (int i = 0; i <= L; i++) {
    data[i]=digitalRead(data_pin);
    delayMicroseconds(5);
    }
}

void Display_Result(){
  for (int i = 0; i <= L; i++) {
    Serial.println(data[i]);
    data[i]=0;
    }
  Serial.println();
  delay(5000);
}

void loop(){
  readDHT();
  delay(1000);  
}
void readDHT() {
  // Go into high impedence state to let pull-up raise data line level and
  // start the reading process.  Pull from HIGH to LOW to tell DHT11 prepare sending dada out
  pinMode(data_pin, OUTPUT);
  //Pull up the data pin prepare for the next step
  digitalWrite(data_pin, HIGH);delay(250);  
  // First set data line low for 20 milliseconds to trigger DHT11 to start generate result.
  // Page 5 in datasheet, it needs >18ms.  
  digitalWrite(data_pin, LOW);delay(20); 

  // End the start signal by setting data line high for 40 microseconds.
  digitalWrite(data_pin, HIGH);
  delayMicroseconds(40);

  // Now start reading the data line to get the value from the DHT sensor.
  pinMode(data_pin, INPUT_PULLUP);  
  delayMicroseconds(50);  // Delay a bit to let sensor pull data line low, and wait till low before get data.   
  Get_Data();
  //Display_Result();  //You can use Serial Plot to see the data like oscolscope
  extract_data();  //Extract data and show end reult.
}

void extract_data(){
  int high=0;
  resultcount=0;
  
  for (int i = 0; i< L-1;i++){
    if ((data[i]==true)){high++;}
    if ((data[i]==false)){
      if (high>0){if(high > extract_data_threadhold){result[resultcount]=true;high=0;resultcount++;}}
      if (high>0){if(high < extract_data_threadhold){result[resultcount]=false;high=0;resultcount++;}}
      }
    }
  
  //for (int j = 0; j<= resultcount;j++){
  for (int j = 0; j< 40;j++){
    result[j]=result[j+1];
    Serial.print(result[j]);
    if (((j+1)/8.0)==int((j+1)/8.0)){Serial.print(" ");}//Add a space every 8 bits
    }  
  Serial.println();   
  //Prepare 2^i.  Arduino IDE do not understand ^ operant 
  double powerof2[16];
  powerof2[0]=1;
  for (int i=1;i<16;i++){
    powerof2[i]=1;
    for (int j=0;j<i;j++){
      powerof2[i]=powerof2[i]*2;  
    }
  }


  //Caculate for DHT11
  if (DHT_type==11){ 
    int startpoint=0;
    double HR=0, TM=0;  
    //Extract HR, temperature, and verify code
    for (int i=0;i<8;i++){
      HR=HR+powerof2[7-i]*(result[i]*1.0);
      TM=TM+powerof2[7-i]*(result[16+i]*1.0);
      //Serial.print("Debug:");Serial.print(" i=");Serial.print(i);Serial.print(" powerof2[15-i]=");Serial.print(powerof2[15-i]);Serial.print(", result[i]=");Serial.print(result[i]);Serial.print(" HR=");Serial.print(HR);Serial.print(" TM=");Serial.print(TM);Serial.println();
      //delay(500);
    }
    
    //Check SUM with 5th byte, ignore overflow
    int VR1=0,VR2=0,VR3=0,VR4=0,VRR=0,VR=0;
    for (int i=0;i<8;i++){
      VR1=VR1+powerof2[7-i]*(result[i]*1.0);    //1st byte
      VR2=VR2+powerof2[7-i]*(result[i+8]*1.0);  //2nd byte
      VR3=VR3+powerof2[7-i]*(result[i+16]*1.0); //3rd byte
      VR4=VR4+powerof2[7-i]*(result[i+24]*1.0); //4th byte
      VR=VR+powerof2[7-i]*(result[i+32]*1.0);   //VR byte      
    }
    VRR=VR1+VR2+VR3+VR4;
    if (VRR>=256)VRR=VRR-256;  //Remove overflow 
    HR=HR;
    TM=TM;
    
    Serial.print("DHT11, HR = ");Serial.print(HR,0);Serial.print("%, Temperature = ");Serial.print(TM,0);Serial.print("C.  Data status is ");
    if ((VRR-VR)==0){Serial.println("verified!");} else {Serial.println("NOT verified!");Serial.println();}    
  }
  //Caculate for DHT22
  if (DHT_type==22){ 
    int startpoint=0;
    double HR=0, TM=0;  
    //Extract HR, temperature, and verify code
    for (int i=0;i<16;i++){
      HR=HR+powerof2[15-i]*(result[i]*1.0);
      TM=TM+powerof2[15-i]*(result[16+i]*1.0);
      //Serial.print("Debug:");Serial.print(" i=");Serial.print(i);Serial.print(" powerof2[15-i]=");Serial.print(powerof2[15-i]);Serial.print(", result[i]=");Serial.print(result[i]);Serial.print(" HR=");Serial.print(HR);Serial.print(" TM=");Serial.print(TM);Serial.println();
      //delay(500);
    }
    
    //Check SUM with 5th byte, ignore overflow
    int VR1=0,VR2=0,VR3=0,VR4=0,VRR=0,VR=0;
    for (int i=0;i<8;i++){
      VR1=VR1+powerof2[7-i]*(result[i]*1.0);    //1st byte
      VR2=VR2+powerof2[7-i]*(result[i+8]*1.0);  //2nd byte
      VR3=VR3+powerof2[7-i]*(result[i+16]*1.0); //3rd byte
      VR4=VR4+powerof2[7-i]*(result[i+24]*1.0); //4th byte
      VR=VR+powerof2[7-i]*(result[i+32]*1.0);   //VR byte      
    }
    VRR=VR1+VR2+VR3+VR4;
    if (VRR>=256)VRR=VRR-256;  //Remove overflow 
    HR=HR/10.0;
    TM=TM/10.0;
    
    Serial.print("DHT22, HR = ");Serial.print(HR,1);Serial.print("%, Temperature = ");Serial.print(TM,1);Serial.print("C.  Data status is ");
    if ((VRR-VR)==0){Serial.println("verified!");} else {Serial.println("NOT verified!");Serial.println();}    
  }
}


Saturday, October 26, 2019

My First Curiosity Development Board Project

Objective: 
  • Familiar with Microchip Curiosity Development Board
  • Install and setup MPLAB X IDE
  • Familiar with DAC, CLC, TMR, PWM and C programming, and basic MPLAB IDE operation.
  • Confirm multiple independent function can be executed in PIC16F1619.

Project Initial Setup:
  1. Hardware:
    1. One "Curiosity Development Board", from Amazon, $20.
    2. One mini USB cable
    3. One Oscilloscope
  2. Software:
    1. Install MPLAB X IDE from https://microchipdeveloper.com/mplabx:installation
    2. Resource: https://www.youtube.com/watch?v=7MqyU0Lxj08

Start A New MPLAB Program (draft note only):
  1. Start MPLAB
  2. File--> Stand along project
  3. Enter device name, in my case, it is PIC16F1619
  4. Select "None" for debug header
  5. Select "Curiosity" in select tool window
  6. Select "XCB" in select compiler window.  Install one if missing.
  7. Provide project name, such as "My test code"
  8. Save "MyConfig.mc3" in "Save MCC Configuration File" window.
  9. Choose "PDIP20" in package pull down tab in "Pin Management Grid View".
  10. MPLAB is ready for programming

Project Objective:
  1. Setup "Saw Shape" signal generator with C code and DAC peripheral.
  2. General two clocks, they are 250KHz and 500Hz with TMR and PWM peripheral.  Setup up one switch to select signal with CLC peripheral but without using CPU's resource.
  3. About two signals process are run simultaneously and independently.
System Environment Setup:
Required Peripherals List

Setup Oscillator with "INTOSC..." and "1MHz".  This internal clock setting affect saw signal frequency directly.

Pin module setting -1

Pin module setting -2

Saw Shape Signal Generator:

Add a simple DAC code
Make sure check two check boxes.
DAC1OUT>>RA0, see the lock symbol in green box.


For 250K and 500 Hz Signal Switch: 
CLC1 setting, need assign "CLCIN0" and "CLCIN1" in above chart.
S1 >> RC4 >>CLCIN0 choose signal between CLCIN1 << PWM3 or CLCIN2<<PWM4
PWM3OUT>>CLCIN1, please note the right two corners' link symbol at RC3 (not show).

PWM4OUT>>CLCIN2, , please note the right two corners' link symbol at RC2 (not show).
CLC1OUT>>RC5, see lock symbol in green box.
Signal Flowchart, use RC4 to control switch to provide either 500Hz or 250KHz output to RC5.


Set clock range and precise Timer Period.  The Timer Period (ex, 2ms) define the timer frequency.
Set duty cycle and source of timer.
Set timer
Set duty cycle and source of timer, when Timer2 using "FOSC" as its source, PWM4 does not show any parameter in PWM parameter.  But, Timer2 using "FOSC/4" as its source, PWM parameters will be populated.  See PWM3 for comparison.  This issue seems not significant issue since both work either or not the PWM parameter has been populated in this window or not.  

Testing Microchip:
Ground: blue wire, RA0: white wire DAC output, RC5, blue wire timer/PWM signal output.
Yellow line: 230Hz saw signal has been generated by DAC and C code.  Its signal is not linear and very noise.  It could be a DAC bug from PIC16F1619.

Blue line: 250K Hz clock when S1 is off/not pressed.
Yellow line from generated by DAC which is quite noise.

S1 switch
Frequency has been dropped to 500Hz clock when S1 is pressed.

Programming Code:
#include "mcc_generated_files/mcc.h"
void main(void)
{
    // initialize the device
    SYSTEM_Initialize();
    uint8_t count=0;
    DAC1_Initialize();
    
    while (1)
    {
        for(count=0; count<=255; count++)
        {
            DAC1_SetOutput(count);
            //__delay_ms(1);
        }
    }
}

Summary:
This 20$ Curiosity Development Board is a very a very different but a powerful  board compare to Arduino.  You can set and execute multiple functions on the same chip and run interdependently.  Many functions can be operated without using CPU resource too. 

My favor peripheral are Timer, PWM and CLC.  They can be set easy without code, and run very fast.  My less favor part is DAC because it is very slow and very noise at the same time.  The worst part is poor signal linearity issue.

I am happy having this board, and will use it on my future project.  However, the first time I try it, I almost give up and return it back to Amazon within the same day.  Here is why:

  1. Cannot find much information on book or internet about this board.
  2. MPLAB X IDE is very complicate.
  3. Many operation concept are new compare to Arduino's.
  4. It is for advance user.  Very steep learning curve.  
  5. Cannot follow some examples since it demos with a different board from Microchip website.  Some peripheral's name, and option are different in their demonstration.
  6. User much familiar with C programming coding.
  7. User must familiar with logical gate.
  8. User must has sufficiency tool to work with this board, such as oscilloscope and etc.
  9. Microchip company do not provide example or detail description on many peripheral such as AT, CCP, CRC, CWG, ZDC, MSSP, FVR and etc.
PIC16F1619 is a power chip, it opens a new application opportunity to me such as HDMI signal handling, and different clock signal source.  But NOT recommend for beginner unless you can find help from experienced user. 

This is my first time using this board.  When writing this blog, I only have about two days trial and error experience on this board.  If you see error, please do not hesitate let me know.  Thanks.  

Resource:

Thursday, October 17, 2019

Garage Door is Still Open Reminder

Project Description.  

To prevent people forget to close their garage door, this project has been build to reminds people if their garage door has been open for more then 10 minutes.

As soon as garage door has been detected as "open", this device will start counting down for 10 minutes.  During counting down period it does not make any sound.  After 10 minutes is up, its buzzer will make "beeps" to reminder owner to close their garage door.

Feature: 
  1. Monitor the garage door is opened or closed.
  1. If the door is open for more then 600 second (10 min), its buzzer sounds reminder alarm.
Component List:
  • 1x Arduino Nano
  • 1x HC-SR04
  • 1x Yellow LED
  • 1x 5V Buzzer 
  • 1x 470 ohm LED resistor, (or any similar resistor)
  • Mini USB power supply
  • Wires
  • Project enclosure box


Schematic:



Result:




Arduino Code:
/* Garage Door Reminder Project
 *
 * Author: Samson Yang
 * Date: 10/19/2019
 *
 * Feature:
 *  1. Monitor the garage door is opened or closed
 *  2. If the door is open more then 600 second (10 min), it sounds "DOOR" mose code as the reminder alarm.
 *
 *  Pin Arrangement:
 *  D4 --> Door status yellow LED
 *  D7 --> US Trigger
 *  D8 --> US Echo
 *  D9 --> Buzzer
 *  
 *  
 *  Regarding <TimeLib.h>, please download zip from https://github.com/PaulStoffregen/Time and then add to Arduino's library
*/
#include <TimeLib.h>
bool buzer = 1;
int Pre_closing_alarm_time=600;   //after 10 minutes sound reminder
int Closed_Door_Threshold=600;  
long int count=0;


// the setup function runs once when you press reset or power the board
  pinMode(8, INPUT);    //Counting pin
  digitalWrite(4, LOW);
  //Test mode when pin 5 is high
  say_hi();
int door_open_time=0;
  //Check how much time the door has been opened
  //Door is opened
  //Send message out
  //Max frequency is 10Hz, or use 100 or higher value
bool check_door(){
  //Triger the signal
  //Measure the distance
  if (count_in<Closed_Door_Threshold)
void say_door(){
void say_door_is_open(){
void say_hi(){
void L(){
void S(){
void _(){


void setup() {
  Serial.begin(9600);
  pinMode(4, OUTPUT);   //Door status indicator
  pinMode(7, OUTPUT);   //Triger pin
  pinMode(9, OUTPUT);   //Alarm pin

  pinMode(5, INPUT);    //Test pin, 1: testing, 0:Normal

  digitalWrite(7, LOW);
  digitalWrite(9, LOW);

  if(digitalRead(5)){
    Pre_closing_alarm_time=3;
  }

}

void loop() {
  int time_gap=0;
  bool door_status = check_door();

  time_gap=now()-door_open_time;

  if (door_status) {
    digitalWrite(4, HIGH); //Turn on door status indicator LED
    if (time_gap>Pre_closing_alarm_time){
      //Remind door is open
      say_door();  //Option 1
      //say_door_is_open();//Option 2 
      //digitalWrite(9, buzer);buzer=!buzer;//Option 3
      }
    }

  else {
    //Door is closed
    digitalWrite(4, LOW); //Turn off door status indicator LED
    door_open_time=now();
    digitalWrite(9, LOW); //Turn off buzer
  }

  //Serial.print("");Serial.print("  ");
  //Serial.print(now());Serial.print("  ");Serial.println(door_open_time);

  delay(100);                    
}

  //if Closed return 0
  //if Opened return 1
  long int count_in=0;

  digitalWrite(7, HIGH);
  digitalWrite(7, LOW);

  //Wait ultra sound echo
  while (!digitalRead(8));

  while (digitalRead(8)){count_in++;}

    {return 1;}
  else
    {return 0;}
}

  //Say mose code D (it means done)
  L();S();S();_(); //D
  L();_();  //O
  L();_();  //O
  S();L();S();//R
  _();_();
}

  //Say mose code D (it means done)
  L();S();S();_();    //D
  L();_();            //O
  L();_();            //O
  S();L();S();_();    //R
  _();_();
  
  S();S();_();        //I
  S();S();S();_();    //S
  _();_();
  
  L();_();            //O
  S();L();L();S();_();//P
  S();_();            //E
  L();S();_();        //N
  _();_();
}

  S();S();S();S();_();S();S();
}

  digitalWrite(9, HIGH);
  delay(300);
  digitalWrite(9, LOW);
  delay(50);
}

  digitalWrite(9, HIGH);
  delay(100);
  digitalWrite(9, LOW);
  delay(50);
}

  digitalWrite(9, LOW);
  delay(300);
}