Showing posts with label arduino. Show all posts
Showing posts with label arduino. Show all posts

Arduino UNO Clone Serial Port CH340/CH341 Drivers for Mac OS X

Recently I tried connecting my newly purchased clone version of the Arduino UNO with the MacBook OS X Yosemite. It turns outs that, nothing gonna detect my serial port. This is because most of the clone versions are using a different chip for USB-to-Serial communication namely CH340/CH341 instead of ATmega16U2.


After hours of work and digging throughout the Internet, I found these drivers for CH341/CH340 is working as expected.

Here how you do it;


Download the drivers and install relevant setup. This package includes, drivers for linux, mac os x and windows.

Download all-in-one drivers pack

One more additional step for Mac OS X users. Disable the kext signing security setting with this command.

sudo nvram boot-args="kext-dev-mode=1"

How to test it;

on the terminal type;

ls /dev/tty*

you should see something like "/dev/tty.wchusbserial1410". In addition, if you open up your Ardunio IDE then goto Tools->Port. You should see something similar to below.




Voice Recognition with Arduino

Recently I built a voice recognition circuit using Arduino. Voice recognition is not that easy with Arduino, It requires more processing and voice analyzing power. Keep in mind that all you have to deal with is a ATMega328P microcontroller running on 16MHz. That's why some modules like BitVoicer is outsourcing the processing power to the PC(Serial/UART or TCP/IP). But modules like uSpeech are completely independent of the PC and can run on its own. Thus, I used uSpeech.

uSpeech is a limited voice recognition software for the AVR. So, you can not directly write the text and expect it to recognize the speech. You need to deal with Phonemes. Hence,a fair amount of patient is needed. List of phonemes you would get is as below;

Phoneme      |   Literal       
--------------------------------------------------------------------
e            | The e sound.
--------------------------------------------------------------------   
h            | The `/sh/` sound. It can also be raised by 
             | `ch`, `j` and `z`
--------------------------------------------------------------------
v            | The `v` sound, occasionally triggered by  , 'z'
             | (may need to be fixed)
--------------------------------------------------------------------
f            | The `f` sound. 
--------------------------------------------------------------------
s            | The 's' sound.
--------------------------------------------------------------------
o            | 'a','o','i','r','l','m','n' and 'u' sounds. 
--------------------------------------------------------------------
' '          | Too Quiet for anything
--------------------------------------------------------------------

In order to get voice input you need a condenser mic with an operational amplifier (op-amp). The easiest way is to buy a MIC module which all are integrated. Try an ebay search.

You can wire it as below. 220 ohm resistors are used.

To Zoom in: click on the image

 This module has LM393 Op-amp chip and Electret condenser microphone.
This is what i bought from the ebay. Just 1.88 USD.
In uSpeech examples, what they actually compared is a single phoneme.It recognizes 6 phonemes (f, e, o, v, s, h). So it is handy for simple commands (if they have one of those phonemes), but it isn’t generalized at all. What i did is, I used a char[32] `buffer` to hold phoneme patterns. Then receiving `buffer` is compared with a list of predefined phoneme patterns. For instance my predefined patterns for green,orange and white are accordingly; "vvvoeeeeeeeofff", "hhhhhvoovvvvf", "hooooooffffffff". To compare which is the closest i used, minimum edit distance(Levenshtein distance). But how to ignore irrelevant patterns? you can use a threshold.

#include <uspeech.h>
#define ledGreen 7
#define ledOrange 6
#define ledWhite 5
#define MIN3(a, b, c) ((a) < (b) ? ((a) < (c) ? (a) : (c)) : ((b) < (c) ? (b) : (c)))
signal voice(A0);
const int BUFFER_MAX_PHONEMES=32;
char inputString[BUFFER_MAX_PHONEMES]; // Allocate some space for the string
byte index = 0; // Index into array; where to store the character
//
const int DICT_MAX_ELEMNTS=3;
char dict[DICT_MAX_ELEMNTS][BUFFER_MAX_PHONEMES]={"vvvoeeeeeeeofff","hhhhhvoovvvvf","hooooooffffffff"};
int LOWEST_COST_MAX_THREASHOLD=20;
void setup(){
  /*
  Phoneme      |   Literal       
  --------------------------------------------------------------------
  e            | The e sound.
  --------------------------------------------------------------------   
  h            | The `/sh/` sound. It can also be raised by 
               | `ch`, `j` and `z`
  --------------------------------------------------------------------
  v            | The `v` sound, occasionally triggered by  , 'z'
               | (may need to be fixed)
  --------------------------------------------------------------------
  f            | The `f` sound. 
  --------------------------------------------------------------------
  s            | The 's' sound.
  --------------------------------------------------------------------
  o            | 'a','o','i','r','l','m','n' and 'u' sounds. 
  --------------------------------------------------------------------
  ' '          | Too Quiet for anything
  --------------------------------------------------------------------
  */
  voice.f_enabled = true;
  voice.minVolume = 1500;
  voice.fconstant = 500;
  voice.econstant = 2;
  voice.aconstant = 4;
  voice.vconstant = 6;
  voice.shconstant = 10;
  voice.calibrate();
  Serial.begin(9600);
  pinMode(ledGreen, OUTPUT); 
  pinMode(ledOrange, OUTPUT); 
  pinMode(ledWhite, OUTPUT);
}

void loop(){
    voice.sample();
    char p = voice.getPhoneme();
    if(p==' ' || index >= BUFFER_MAX_PHONEMES){
      if(strLength(inputString)>0){
         Serial.println("received:"+String(inputString));
         parseCommand(inputString);
         inputString[0]=0;//clear string char array
         index=0;
      }
    }else{
      //printArray(voice.arr);
      inputString[index] = p; // Store it
      index++;
      inputString[index] = '\0'; // Null terminate the string
    }
}

char* guessWord(char* target){
  int len = strlen(target);//held target length
  
  for(int i=0;i<DICT_MAX_ELEMNTS;i++){
    //simple validation
    if(dict[i]==target){
      return dict[i];
    }
  }
  
  unsigned int cost[DICT_MAX_ELEMNTS];//held minimum distance cost

  //calculating each words cost and hits
  for(int j=0;j<DICT_MAX_ELEMNTS;j++){//loop through the dictionary
      cost[j]=levenshtein(dict[j],target);
      Serial.println("dict[j]="+String(dict[j])+" target="+String(target)+" cost="+String(cost[j]));
  }    
  
  //Determining lowest cost but still all letters in the pattern hitting word
  int lowestCostIndex=-1;
  int lowestCost=LOWEST_COST_MAX_THREASHOLD;
  for(int j=0;j<DICT_MAX_ELEMNTS;j++){//loop through the dictionary
    //Serial.println("dict[j]="+dict[j]+" dict[j].length()="+String(strlen(dict[j]))+" cost="+String(cost[j])+" hits="+String(hits[j])+" j="+String(j));
    if(cost[j]<lowestCost){
      lowestCost = cost[j];
      lowestCostIndex=j;
    }
  }
  
  //Serial.println("lowestCostIndex="+String(lowestCostIndex)+" lowestCost="+String(lowestCost));
  
  if(lowestCostIndex>-1){
    //Serial.println("lowestCost="+String(lowestCost)+" lowestCostIndex="+String(lowestCostIndex));
    return dict[lowestCostIndex];
  }else{
    return "";
  }
}

void parseCommand(char* str){
  char *gWord = guessWord(str);
  Serial.println("guessed :"+String(gWord));
  if(gWord==""){
    return;
  }else if(gWord==dict[0]){
    digitalWrite(ledGreen, HIGH);
    digitalWrite(ledOrange, LOW);
    digitalWrite(ledWhite, LOW);
  }else if(gWord==dict[1]){
    digitalWrite(ledGreen, LOW);
    digitalWrite(ledOrange, HIGH);
    digitalWrite(ledWhite, LOW);
  }else if(gWord==dict[2]){
    digitalWrite(ledGreen, LOW);
    digitalWrite(ledOrange, LOW);
    digitalWrite(ledWhite, HIGH);
  }
}

unsigned int levenshtein(char *s1, char *s2) {
    unsigned int s1len, s2len, x, y, lastdiag, olddiag;
    s1len = strlen(s1);
    s2len = strlen(s2);
    unsigned int column[s1len+1];
    for (y = 1; y <= s1len; y++)
        column[y] = y;
    for (x = 1; x <= s2len; x++) {
        column[0] = x;
        for (y = 1, lastdiag = x-1; y <= s1len; y++) {
            olddiag = column[y];
            column[y] = MIN3(column[y] + 1, column[y-1] + 1, lastdiag + (s1[y-1] == s2[x-1] ? 0 : 1));
            lastdiag = olddiag;
        }
    }
    return(column[s1len]);
}

int strLength(char const* s) {
    int i = 0;
    while (s[i] != '\0' && s[i] != ' ')
        ++i;

    return i;
}

How to install uSpeech? download the zip file from here. Then, extract the files in to your arduino `Library` folder. In my case, it is on 'C:\Program Files (x86)\Arduino\libraries'. It should be in a separate folder, let's say 'uSpeech'. Finally, restart the Arduino IDE.


Coding more than Four `7segment` display panels on Arduino

Recently I coded a seven segment display for a friend of mine. Hope this might be helpful for wiring purposes. My task was to correctly display 5 digit number on 5 `seven segments`. So, most of the examples out there including this module support only up to 4 segments. So, I wrote my own code from the scratch.


This is the code, I wrote:

const int SEGMENT_RENDER_DELAY_MS=5;//segment rendering delay
const int MAX_NUM_SEGMENTS=5;//store maximum segments
//
String inputString = ""; // a string to hold incoming data
String displayStr="hellO"; //current display string (keep empty to display all zero's!)
//unsigned long digit; // display digit, if you are dealing only with numbers
//           a
//        =======
//      ||       ||
//    f ||       || b
//      ||   g   ||
//        =======
//      ||       ||
//    e ||       || c
//      ||   d   ||
//        =======
 
byte seven_seg[16][9] = { {0,0, 1,1,1,1,1,1,0 },  // = 0
                         {0,0, 0,1,1,0,0,0,0 },  // = 1
                         {0,0, 1,1,0,1,1,0,1 },  // = 2
                         {0,0, 1,1,1,1,0,0,1 },  // = 3
                         {0,0, 0,1,1,0,0,1,1 },  // = 4
                         {0,0, 1,0,1,1,0,1,1 },  // = 5
                         {0,0, 1,0,1,1,1,1,1 },  // = 6
                         {0,0, 1,1,1,0,0,0,0 },  // = 7
                         {0,0, 1,1,1,1,1,1,1 },  // = 8
                         {0,0, 1,1,1,0,0,1,1 },   // = 9
                         {0,0, 1,0,0,1,1,1,1 },   // = e
                         {0,0, 0,0,0,0,1,0,1 },   // = r
                         {0,0, 0,0,1,1,1,0,1 },   // = o
                         {0,0, 1,1,1,1,1,1,0 },   // = O
                         {0,0, 0,1,1,0,1,1,1 },   // = h
                         {0,0, 0,0,0,1,1,1,0 }   // = l
                         };
                         
byte firstSegmentPos = 0, secSegmentPos = 0,thirdSegmentPos = 0, fourthSegmentPos = 0, fifthSegmentPos = 0;
//
int pin;

void setup() {    
   // initialize serial:
  Serial.begin(9600);
  // reserve 200 bytes for the inputString:
  inputString.reserve(200);
  
  //define output pins
  pinMode(2, OUTPUT);   
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
  
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);
}

void loop() {
   updateSegments();
}

/*
  SerialEvent occurs whenever a new data comes in the
 hardware serial RX.  This routine is run between each
 time loop() runs, so using delay inside loop can delay
 response.  Multiple bytes of data may be available.
 */
void serialEvent() {
 
  while (Serial.available()) {
    // get the new byte:
    char inChar = (char)Serial.read(); 

    //if linebreak found, parse it
    if (inChar == '\r'||inChar == '\n') {
      //we got a break, parse string
      parseCode(&inputString);
      inputString="";
    }else{
      //append the character received
      inputString += inChar;
    }

  }
}

/**
* Parses one line of transaction
* If validation failed-> error displayed Or value displayed
**/
void parseCode(String* receivedLine){
  String line = *receivedLine;
  line.trim();//remove spaces
  //
  int lineLength=line.length();//length of the line received

  String rv="";
  if(lineLength <= MAX_NUM_SEGMENTS){
    rv=line;//set to display value
    //nSuccessTrans++;
  }else{
    rv="error";
    //nFailTrans++;
  }
  displayStr=rv;//set to display value
  
  //digit = displayStr.toInt();// if you are dealing only with numbers
}

/**
* Updates Display Segments
**/
void updateSegments(){
  /*
  fifthSegmentPos = digit / 10000;
  digit %= 10000;
  fourthSegmentPos = digit / 1000;
  digit %= 1000;
  thirdSegmentPos = digit / 100;
  digit %= 100;
  secSegmentPos = digit / 10;
  firstSegmentPos = digit % 10;
  */
  firstSegmentPos = GetSegmentCharPos(&displayStr,1); 
  secSegmentPos = GetSegmentCharPos(&displayStr,2); 
  thirdSegmentPos = GetSegmentCharPos(&displayStr,3); 
  fourthSegmentPos = GetSegmentCharPos(&displayStr,4); 
  fifthSegmentPos =  GetSegmentCharPos(&displayStr,5);

  //Serial.println("first segment");
  digitalWrite(9,HIGH);   
  digitalWrite(10,LOW); 
  digitalWrite(11,LOW);   
  digitalWrite(12,LOW);   
  digitalWrite(13,LOW);     
  displaySegmentChar(&firstSegmentPos);
  delay(SEGMENT_RENDER_DELAY_MS);
  
  //Serial.println("second segment");
  digitalWrite(9,LOW);   
  digitalWrite(10,HIGH);
  digitalWrite(11,LOW);   
  digitalWrite(12,LOW);   
  digitalWrite(13,LOW);   
  displaySegmentChar(&secSegmentPos);
  delay(SEGMENT_RENDER_DELAY_MS);
  
  //Serial.println("third segment");
  digitalWrite(9,LOW);   
  digitalWrite(10,LOW);
  digitalWrite(11,HIGH);   
  digitalWrite(12,LOW);   
  digitalWrite(13,LOW);   
  displaySegmentChar(&thirdSegmentPos);
  delay(SEGMENT_RENDER_DELAY_MS);
  
  //Serial.println("fourth segment");
  digitalWrite(9,LOW);   
  digitalWrite(10,LOW);
  digitalWrite(11,LOW);   
  digitalWrite(12,HIGH);   
  digitalWrite(13,LOW);   
  displaySegmentChar(&fourthSegmentPos);
  delay(SEGMENT_RENDER_DELAY_MS);
  
  //Serial.println("fifth segment");
  digitalWrite(9,LOW);   
  digitalWrite(10,LOW);
  digitalWrite(11,LOW);   
  digitalWrite(12,LOW);   
  digitalWrite(13,HIGH);   
  displaySegmentChar(&fifthSegmentPos);
  delay(SEGMENT_RENDER_DELAY_MS);
}

/**
* Returns position of the rendering `segment character`
**/
static int GetSegmentCharPos(String* strNum, int pos){
        String strNumber = *strNum;
        if(strNumber.length()==0) return 0;
        if(strNumber.length()>=pos){
           char c = strNumber.charAt(strNumber.length()-pos);
           if(c=='e') return 10;//e
           if(c=='r') return 11;//r
           if(c=='o') return 12;//o
           if(c=='O') return 13;//O
           if(c=='h') return 14;//h
           if(c=='l') return 15;//l
           if(c-'0'>0) return c-'0';//digit [0-9]
        }
        return 0;
}
/**
* Sketches single digit
**/
void displaySegmentChar(byte* pos)  {
   int digitPos = *pos;
   for(pin = 2;pin < 9; pin++) {
        digitalWrite(pin,seven_seg[digitPos][pin]);
   }
}
 This code holds to be displayed code as a String object i.e. `displayStr` because i needed to display non-numeric characters as well(e.g. `error`). If you only need digits, you may use unsigned long called `digit`. And you can use following code to get segment numbers;
  fifthSegmentPos = digit / 10000;
  digit %= 10000;
  fourthSegmentPos = digit / 1000;
  digit %= 1000;
  thirdSegmentPos = digit / 100;
  digit %= 100;
  secSegmentPos = digit / 10;
  firstSegmentPos = digit % 10;

ATMega328P Malfunctioning

ATMega328P Malfunctioning
As i mentioned in my previous post, I ordered a chinese Arduino UNO R3 version board. It is pretty cool compared to genuine more additional features. But you'd not get default LED light connected to the pin 13 and Tx Rx LEDs.

This is my experience with Arduino UNO R3 version board. Since I already worked with Arduino boards at Uni, I was little bit surprised since once you connected USB cable to the PC and If you correctly installed Arduino IDE(with drivers) but STILL it is not detecting my board.

After spending couple of times, I realized that these version boards using a different chip for USB-to-Serial communication namely CH340/CH341 instead of ATmega16U2. Finally i installed CH340 Serial drivers and it connected perfectly.

Then i got this "Blink" example from the IDE and tried to upload it.But unfortunately It gave following error.



This is because Arduino IDE tells ATMEGA328P's bootloader to write some bytes, then IDE tries to read again from the bootloader to verify. But it fails. which means bootloader unable to write to its flash. So i tried several times several options which is on the Internet; but never worked out. 

Finally i concluded that my ATMEGA328P is no longer usable.

Arduino Introduction

I recently started developing with Arduino. In the old days we had to develop everything from the scratch. Arduino and other DIY kits makes it lot easier. If you are in two minds whether to buy Arduino or `Raspberry Pi`. I would simply say, If your project is something to do with hardware Arduino is most suitable. If your project is something to do with software more then `Raspberry Pi` would be a better choice. But notice that you need to think other factors as well.

I ordered chineese Ardunio UNO R3 version board. Almost every functionality as original UNO and few things more. major difference is CH340 chip for USB-Serial instead of ATmega16U2.So it won't be automatically detect and install drivers for you. You need to install CH340 drivers first. Main programmable chip is same ATMega328P.

this is how Arduino UNO clone look likes
But never trust, a chinese... received a malfunctioning ATMEGA328P microcontroller. So I went pettah market(Sri Lanka), ATMega328P microcontroller costs 390 LKR only. Finally seller agreed to refund 5 USD.

However i bought another Arduino UNO from here SL, costed me around 1600 LKR. Atleast it is more look like genuine.

this is another Arduino UNO more or less look like genuine
Following are the specifications of Arduino UNO R3;


MicrocontrollerATMega328P 
Clock Speed16MHz (but ATMega328P supports 20MHz)
Flash Memory32K (ATMega328P) 0.5KB used by bootloader
EEPROM1K (ATMega328P)
SRAM2K (ATMega328P)
Digital I/O Pins14(in which 6 PWM output supported)
Analog Output Pins6
DC current per I/O pin40mA
DC current per 3.3V pin50mA
Operating Voltage5V
Input Voltage6-20V but 7-12V recommended

Arduino UNO R3 Pinout diagram