古人智慧

Just Do it!
上士聞道,勤而行之;中士聞道,若存若亡;下士聞道,大笑之。不笑,不足以爲道。
~ 道德經 41

「實現夢想不是追逐成功,而是在於賦予生命意義,人生中的每個決定與聲音都有其重要含義。」"The key to realizing a dream is to focus not on success but on significance — and then even the small steps and little victories along your path will take on greater meaning."
電視名人-歐普拉·溫芙蕾(OPRAH WINFREY)

搜尋此網誌

Translation

2018年5月13日 星期日

[母親節] 西瓜盆花~~

2018母親節這天,老大一早人不見蛋,應該會女友了。。。
晚上抱回這一盆西瓜裝一堆花,只有他老媽才會開心吧~~~

看看成果

2018年5月6日 星期日

[Altera Cyclone IV] 開發板 Quartus II & ModelSim installation

這幾天整理工作室,將工具、零件、Arduino、ESP8266等分類與歸位。
在角落發現這片FPGA開發板,估計應該是3年多前買的。。。
上網找相關資料,現在AI領域已經不再以GPU爲重點。FPGAASIC將引領風潮~~~
嗯,看來要再把這片開發板拿來深入學習。。。。
在網上查到資料,Intel在2015將Altera併購,而且開發軟件工具QuartusII已經升級到17.1. 立馬下載Linux的版本並安裝到我Ubuntu16.04 LTS 64bit系統。這套開發軟件包約7GB,我花了將近2小時下載,安裝過程算是順利。
當QuartusII在我Ubuntu跑起來,真是感動阿。。。馬上把點亮LED的範例輸入並compile成功!正當陶醉時,準備把code下載到Cyclone IV卻發現認不到USB的下載器!!!
還好網上神人多,Google就可以找到方法,記錄如下:

lsusb

cd /etc/udev/rules.d

sudo nano 51-usbblaster.rules
把這行輸入並存檔

ATTR{idVendor}=="09fb", ATTR{idProduct}=="6001", MODE="666"
然後下指令建立這USB下載器的node

sudo udevadm control --reload-rules
再來觀察log確定USB下載器是否正確被load

dmesg | tail
因爲沒及時將訊息存下,無法分享。
當一切都OK,準備學習更多Verilog!
但是,過程發現一個很好用的工具ModelSim竟然無法被啓動,終於花了2天找到解決方案,這問題是發生在這套ModelSim是用32bits環境,但是我Ubuntu是64bits,缺了一些32bits的library,只要裝好一切OK,太帥了!!!
ModelSim需要的32bits安裝

sudo apt-get install libc6:i386 libX11.so.6:i386 libxext6:i386 libxft2:i386 lib32ncurses5 libstdc++6:i386
如果發生source code的字型很小,那就要用這個方式解決,因爲在menu bar找不到設定。
Modelsim screenshot
I ran into this too in Ubuntu 16.04 LTS with modelsim 10.5 ASE. I fixed it as follows:

Open ~/.modelsim (use "nano ~/.modelsim" in terminal)
Find: PrefDefault = ... textFontV2 {Verdana 12} (the name of the font may differ)
Change 12 to -12, so it will looks like this: textFontV2 {Verdana -12}
Save ~/.modelsim (Ctrl+O and then Enter)
Reopen modelsim

2018年5月2日 星期三

[iRobot] Roomba 860 UART connect test

2年前家裡買這台掃地機iRobot Roomba 860

開始使用時覺得新鮮,但是總覺得他跑的路徑都怪怪的,不是很有效率與邏輯。However,這幾年就過了沒想這麼多。今天,女主人反映860的邊刷斷了,就是這個

《800系列的邊刷》
看來只能買新的,我就在想這台機器是否有機會升級韌體。Google很多資訊似乎需要買一台Firmware 升級器 OSMO,仔細看了一下,這版號是2012-08-22-1754!!! 似乎很舊了~~
但是,我這台860到底Firmware版號是多少? 如何check?
好問題,開始動手解決問題。。。

資料搜集

1.iRobot有無特殊接口?
2.如果有,要如何讀取?
資料顯示,iRobot在面板上有個隱藏的接口,在把手下面,有個橡皮蓋打開就出現。

《翻開把手》

《翻開橡皮蓋》

《細部觀察接口》

工具與設備

1.PC
2.Uart轉USB
3.單芯線

《Uart轉USB》

實驗過程

找到接口定義後分析,這接口應該是UART(串口),就依定義把先接好,如下圖。
因為找不到這種特殊接頭,雖然可以用PS/2接頭改造,但是我手上還是沒這種接頭,只好用克難式用單芯線來取代。

《用單芯線來接》

《插入接頭剛好》
當線接好之後,把Uart-USB插入PC,打開Uart terminal設定
Baudrate: 115200
Data bits: 8
Parity: None
Stop bits: 1
Flow control: None
然後連上Uart port,按下iRobot中間的啟動鍵,可以從Terminal看到開機訊息如下:

看來,我這台Firmware版本是比較新,應該不需要升級。

後續

其實開可以下命令給iRobot,我查到資料是有一份iRobot® Roomba 500 Open Interface (OI) Specification,有寫Python code透過USB下command,這部分下次有空再分享。

2018年4月29日 星期日

[Robot] 全3D打印 履帶機器人DIY

坦克車履帶傳動系統是個很棒的設計,可以在複雜與惡劣的地形上快速行進,用在robot上應該是適用的。
最近在Thingiverse看到一個神人作品SMARS作品是可以全3D打印的履帶與機身, 而且使用的小型馬達N20剛好手上有,真是太棒了,可以解解這種傳動系統的渴。。。

作品背景說明

要啟動一個項目,電控板是核心,有幾點必須考慮:
1.PCB尺寸
2.有多少GPIO可以控制外接模塊或馬達
3.operation電壓
再來就是周邊模塊的選擇,例如直流DC馬達要搭配馬達驅動板。
遙控器是相當麻煩的部分,我是選擇用jjRobots公司搭配OSC協議透過Wifi UDP傳送,OSC主要訴求是即時傳送與接收,這對遙控車是非常必要的選擇。
之前做4足Spider用HTML來控制,就非常不順暢卡卡的,甚至會突然HTML package會漏掉。
最後,電源系統設計,從電池電壓、尺寸與重量,DC-DC降壓板的電流輸出,這些都足以讓項目卡關,難以持續。
從上述的分析,很容易瞭解這次為何選擇TinyPlan。這片TinyPlan是以ESP8266為核心,並搭載兩顆2.4v 750F電容式快充鋰電池,串聯起來可以提供4.8v,1C放電。並提供8個GPIO,4根pin接馬達驅動板,2根接UltraSonic sensor綽綽有餘,是個非常好的選擇。

作品展示

零件表與設備工具

BOM list:

設備:

3D 打印機
3D 結構

設計階段

3D 結構列印與組裝


《用2mm鑽頭通一下比較好安裝》

《TinyPlan+L9113S》

Arduino軟件設計

參考jjRobots的OSC來開發(https://github.com/jjrobots/B-ROBOT_EVO2/tree/master/Arduino)
主程式如下:
/*
   RegisHsu 2018-04-28
   TinyPlan ESP8266 module + L9110S + UltraSound
   Controller jjrobots - www.jjrobots.com

   v01:
      initial version
   v02:
      add UltraDonic sensor
*/

#include <ESP8266WiFi.h>
#include "RHROBOTS_OSC.h"
#include "RHROBOTS_BROBOT.h"

#define BAUDRATE 250000

// TinyPlan Port define
#define PIN_D1 14
#define PIN_D2 12
#define PIN_D3 13
#define PIN_D4 15
#define PIN_D5 16
#define PIN_D6 5
#define PIN_D7 4
#define PIN_D8 2

//N20 Motor pin define
#define MOTOR_M1_S1 PIN_D1
#define MOTOR_M1_S2 PIN_D2
#define MOTOR_M2_S1 PIN_D5
#define MOTOR_M2_S2 PIN_D6

//Ultrasonic sensor pin define
#define ULTRA_TRIG PIN_D3
#define ULTRA_ECHO PIN_D7

// NORMAL MODE PARAMETERS (MAXIMUN SETTINGS)
#define MAX_THROTTLE 2400
#define MAX_STEERING 2400
#define MAX_CONTROL_OUTPUT 1024

uint8_t loop_counter;       // To generate a medium loop 40Hz
uint8_t slow_loop_counter;  // slow loop 2Hz
uint8_t sendBattery_counter; // To send battery status

long timer_old;
long timer_value;
int debug_counter;
float debugVariable;
float dt;

int16_t motor1;
int16_t motor2;

bool newControlParameters = false;
bool modifing_control_parameters = false;

uint8_t mode;  // mode = 0 Normal mode, mode = 1 Pro mode (More agressive)

float throttle;
float steering;
float max_throttle = MAX_THROTTLE;
float max_steering = MAX_STEERING;
float control_output;

// Ultrasonic
volatile long duration;
volatile int distance;
volatile int echo_interr_flag, echo_obstacle;
volatile long t_echo_s, t_echo_e;
long t_auto_last, t_auto_curr;
int echo_trig_sw;

void echo_interr(void)
{
  echo_interr_flag = 1 - echo_interr_flag;
  if (echo_interr_flag)
    t_echo_s = micros();
  else
  {
    t_echo_e = micros();
    duration = t_echo_e - t_echo_s;
    distance = duration * 0.034 / 2;
    if (distance < 12) //12cm
      echo_obstacle = 1;
    else
      echo_obstacle = 0;
  }
}

void echo_trigger(void)
{
  echo_trig_sw = 1;
  // Clears the trigPin
  digitalWrite(ULTRA_TRIG, LOW);
  delayMicroseconds(2);

  // Sets the trigPin on HIGH state for 10 micro seconds
  digitalWrite(ULTRA_TRIG, HIGH);
  delayMicroseconds(10);
  digitalWrite(ULTRA_TRIG, LOW);
  echo_interr_flag = 0;
  echo_obstacle = 0;
}

void auto_pilot(void)
{
  t_auto_curr = millis();
  if ((t_auto_curr - t_auto_last) > 80)
  {
    t_auto_last = t_auto_curr;
    //Serial.println("Trig");
    echo_trigger();
  }

  if (echo_obstacle)
  {
    echo_obstacle = 0;
    //Serial.println("obstacle");
    //stop
    setMotorSpeedM1(0);
    setMotorSpeedM2(0);
    delay(800);
    //back
    setMotorSpeedM1(-motor1);
    setMotorSpeedM2(-motor2);
    delay(600);
    //turn left
    setMotorSpeedM1(motor1);
    setMotorSpeedM2(-motor2);
    delay(600);
    //stop
    setMotorSpeedM1(0);
    setMotorSpeedM2(0);
    delay(500);
  }
  setMotorSpeedM1(motor1);
  setMotorSpeedM2(motor2);
}

// Set speed of Stepper Motor1
// tspeed could be positive or negative (reverse)
void setMotorSpeedM1(int16_t tspeed)
{
  if (tspeed >= 0)
  {
    analogWrite(MOTOR_M1_S1, 0);
    analogWrite(MOTOR_M1_S2, tspeed);
  }
  else
  {
    analogWrite(MOTOR_M1_S1, -tspeed);
    analogWrite(MOTOR_M1_S2, 0);
  }
}

// Set speed of Stepper Motor2
// tspeed could be positive or negative (reverse)
void setMotorSpeedM2(int16_t tspeed)
{
  if (tspeed >= 0)
  {
    analogWrite(MOTOR_M2_S1, 0);
    analogWrite(MOTOR_M2_S2, tspeed);
  }
  else
  {
    analogWrite(MOTOR_M2_S1, -tspeed);
    analogWrite(MOTOR_M2_S2, 0);
  }
}

void setup() {
  // put your setup code here, to run once:
  // init motors
  pinMode(MOTOR_M1_S1, OUTPUT);
  pinMode(MOTOR_M1_S2, OUTPUT);
  pinMode(MOTOR_M2_S1, OUTPUT);
  pinMode(MOTOR_M2_S2, OUTPUT);

  //init Ultrasonic sensor
  echo_interr_flag = 0;
  t_auto_last = 0;
  echo_obstacle = 0;
  pinMode(ULTRA_TRIG, OUTPUT); // Sets the trigPin as an Output
  pinMode(ULTRA_ECHO, INPUT_PULLUP); // Sets the echoPin as an Input
  attachInterrupt(digitalPinToInterrupt(ULTRA_ECHO), echo_interr, CHANGE);
  Serial.begin(BAUDRATE); // Serial output to console
  //Regis
  OSC.UDP_Init();

  Serial.println("BROBOT by JJROBOTS v2.2");

  // STEPPER MOTORS INITIALIZATION
  Serial.println("DC motors initialization...");
  // pre-action
  echo_trigger();
  // Little motor vibration to indicate that robot is ready
  setMotorSpeedM1(0);
  setMotorSpeedM2(0);
  for (uint8_t k = 0; k < 3; k++)
  {
    setMotorSpeedM1(1000);
    setMotorSpeedM2(1000);
    //BROBOT.moveServo1(SERVO_AUX_NEUTRO + 5);
    delay(100);
    setMotorSpeedM1(-1000);
    setMotorSpeedM2(-1000);
    //BROBOT.moveServo1(SERVO_AUX_NEUTRO - 5);
    delay(100);
  }
  setMotorSpeedM1(0);
  setMotorSpeedM2(0);

  for (int i = 0; i < 3; i++)
  {
    // pre-action
    echo_trigger();
    delay(200);
  }
  // OSC initialization
  OSC.fadder1 = 0.5;
  OSC.fadder2 = 0.5;

  Serial.println("Let's start...");
  mode = 0;
}

void loop() {

  //Regis
  OSC.MsgRead();  // Read UDP OSC messages
  if (OSC.toggle1 == 1)
  {
    motor1 = -780;
    motor2 = -780;
    auto_pilot();
  }

  if (OSC.newMessage)
  {
    OSC.newMessage = 0;
    //Regis
    if (OSC.page == 1) // Get commands from user (PAGE1 are user commands: throttle, steering...)
    {
      //OSC.newMessage = 0;
      throttle = (OSC.fadder1 - 0.5) * max_throttle;
      // We add some exponential on steering to smooth the center band
      steering = OSC.fadder2 - 0.5;
      if (steering > 0)
        steering = (steering * steering + 0.5 * steering) * max_steering;
      else
        steering = (-steering * steering + 0.5 * steering) * max_steering;

      motor1 = throttle - steering;
      motor2 = throttle + steering;
      motor1 = constrain(motor1, -MAX_CONTROL_OUTPUT, MAX_CONTROL_OUTPUT);
      motor2 = constrain(motor2, -MAX_CONTROL_OUTPUT, MAX_CONTROL_OUTPUT);
      setMotorSpeedM1(motor1);
      setMotorSpeedM2(motor2);
    }
  } // End new OSC message
}

測試


《使用jjRobots的遙控App》

《加上UltraSonic sensor》

2018年4月22日 星期日

[NodeMCU] L293D Motor shield board 電機驅動擴展板 應用

NodeMCU來控制馬達電機做個履帶機器人是最近想要做的項目,雖然是很普通的項目,但是加上攝像顯示屏還有一些sensors應該是個很好的運動平台。

《把NodeMCU直接插上》

《插上後狀態》
看上這片L293D擴展板是因為可以直接用NodeMCU,而且把全部的I/O都外接與VccGround規劃在一起,是個不錯的多功能的擴展板。
但是~~收到這板子,要命了。。。。這擴展板的footprint尺寸與手上的NodeMCU不適配,手上的NodeMCU比較寬將近2mm。也因為這樣,這片擴展板就被擺在桌角。
今天剛好瞄到,拿到手上把玩了一下,突然靈光一現,用排針座!!!
是的,90°排針座的尺寸剛剛好,太棒了~~~
立馬用烙鐵焊上,大小通吃!!

《腳跑出床外。。。》

《90°排針座剛好》

《呵呵,大小通吃》
既然接上了,那就繼續把這擴展板徹底研究清楚。
找遍x寶都找不到這片擴展板的原理圖,只好用萬用電錶一條條量測,還好沒很多條。
下圖就是示意圖。
《L293D接腳與擴展版圖》

《正面解說圖》

《尺寸圖》

測試工具與設備

NodeMCU x1
L293D Motor Shield board 電機擴展板 x1
5-12v電機馬達
萬用電錶 x1
電源供應器 x1

測試code

/*
 * RegisHsu 2018-04-22
 * The L293D motor shield board for NodeMCU
 * Enable 1-2: nodemcu pin 1
 * Enable 3-4: pin 2
 * 1A: pin 3
 * 2A: internal control
 * 3A: pin 4
 * 4A: internal xontrol
 */

#define __NODEMCU__

#ifdef __NODEMCU__
// NodeMCU與ESP8266 pin對照表
// These are the pins for all ESP8266 boards
//      Name   GPIO    Function
#define PIN_D0  16  // WAKE
#define PIN_D1   5  // User purpose
#define PIN_D2   4  // User purpose
#define PIN_D3   0  // FLASH mode at boot time
#define PIN_D4   2  // TXD1 (Note: low on boot means go to FLASH mode)
#define PIN_D5  14  // HSCLK
#define PIN_D6  12  // HMISO
#define PIN_D7  13  // HMOSI  RXD2
#define PIN_D8  15  // HCS    TXD0
#define PIN_D9   3  // RXD0
#define PIN_D10  1  // TXD0

#define E1 PIN_D1  // Enable Pin for motor 1, every pin can be PWM in ESP8266
#define E2 PIN_D2  // Enable Pin for motor 2

#define I1 PIN_D3  // Control pin 1 for motor 1
#define I3 PIN_D4  // Control pin 1 for motor 2

#else
//      Name   GPIO    Function
#define PIN_D0   0
#define PIN_D1   1
#define PIN_D2   2
#define PIN_D3   3
#define PIN_D4   4
#define PIN_D5   5
#define PIN_D6   6
#define PIN_D7   7
#define PIN_D8   8
#define PIN_D9   9
#define PIN_D10  10

#define E1 PIN_D8  // Enable Pin for motor 1
#define E2 PIN_D9  // Enable Pin for motor 2

#define I1 PIN_D3  // Control pin 1 for motor 1
#define I3 PIN_D4  // Control pin 1 for motor 2

#endif

void setup()
{
  Serial.begin(115200);

  pinMode(E1, OUTPUT);
  pinMode(E2, OUTPUT);

  pinMode(I1, OUTPUT);
  pinMode(I3, OUTPUT);
  Serial.println("Setup already...");
}

void L_Wheel(int sp, bool s1, bool s2)
{
  analogWrite(E2, sp); // Enable as speed
  digitalWrite(I3, s1);
}

void R_Wheel(int sp, bool s1, bool s2)
{
  analogWrite(E1, sp); // Enable as speed
  digitalWrite(I1, s1);
}

int spd = 1020;
void loop()
{
  Serial.print("SPD=");
  Serial.println(spd);
  //FF
  Serial.println("FF...");
  L_Wheel(spd, HIGH, LOW);
  R_Wheel(spd, HIGH, LOW);
  delay(2000);
  L_Wheel(0, LOW, LOW);
  R_Wheel(0, LOW, LOW);
  delay(2000);

  //RR
  Serial.println("RR...");
  L_Wheel(spd, LOW, HIGH);
  R_Wheel(spd, LOW, HIGH);
  delay(2000);
  L_Wheel(0, LOW, LOW);
  R_Wheel(0, LOW, LOW);
  delay(2000);

  //FR
  Serial.println("FR...");
  L_Wheel(spd, HIGH, LOW);
  R_Wheel(spd, LOW, HIGH);
  delay(2000);
  L_Wheel(0, LOW, LOW);
  R_Wheel(0, LOW, LOW);
  delay(2000);

  //RF
  Serial.println("RF...");
  L_Wheel(spd, LOW, HIGH);
  R_Wheel(spd, HIGH, LOW);
  delay(2000);
  L_Wheel(0, LOW, LOW);
  R_Wheel(0, LOW, LOW);
  delay(2000);
  spd = spd - 100;
  if (spd < 200)
    spd = 1020;
}

視頻分享