VPython Device Ball-trow1.py    
 * 這是 Ball-throw1 的 DA, 但這次是要把程式放自己電腦上, 必要時可略作修改 ! (我們沒權限修改Server上的檔案)
    ╰┈➤ (稍後也會教你如何稍做修改丟入 EduTalk 用手機/電腦上的遙控器控制;包括 Ball-throw1 和 -throw2)
    需要一個 .py 檔案, 以及一個會 include 各 .js 檔案的 HTML 檔案(廢話:-)。  
   ==> 需要修改的 .js 當然要抓回自己電腦, 這時也要改 HTML 內變成 include 自己電腦上的 .js (廢話)
    ==> 基本上抓四個可能要改的檔案到自己電腦: (1)HTML 檔案, (2)dai.js, (3)dan.js, (4)VPython 的 .py 檔案
        (其它都 "引用" 網路上任何server, 例如 https://class.iottalk.tw 或其它 server, 或 CDN 上的檔案即可, 只要內容一樣!)
    Why CDN ?❓ ==> CDN for JavaScript Libraries: Benefits and Drawbacks
    HTML 檔案可用 View Source 看到(或用'檢查'更好用), 稍為修改一下以便從自己電腦抓 dai.js
    * (可先看看我以下完整說明影片中關於如何從 iottalk server 看網頁時利用"檢查"偷回 HTML 和要改的 .js 以及 .py 檔案)

** 如果要在自己電腦用Python+Flask弄個網站 serve 該些檔案(很簡單),
          可以點這看看範例 w80.py (會跳到後面)   或   點這參考以前說過的 iottalk.vip/6/

    ⚡==> 如果不弄網站,你可能需要開 CMD / PowerShell 這樣把瀏覽器跑起來:
      start chrome --user-data-dir="." --disable-web-security
      因為, 直接雙擊 .html 檔案會被 Google Chrome 以安全理由擋下不給執行 VPython 的 DA !!
*** 詳細可以 點這看影片說明 (善用影片下方說明與子題連結)
  以下是我稍為修改過的 Ball-throw1.py
** Original code can be found at https://class.iottalk.tw/da/vp/py/Ball-throw1.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
### Ball-throw1.py   ## coding: utf-8 #紅色字體的 code 是我加上去的
axis = []
labels = []
is_running = False
speed = 0
angle = 45*2*3.14/360 
height = 40
z = 68
preloadAudio('Startup.wav')
preloadAudio('chord.wav')
preloadAudio('gj.wav')

def axisInit():
    global axis, labels
    a = 0
    b = 0
    c = 500
    d = 200
    axis.append(arrow(pos=vec(a,b,0), axis=vec(c+50,0,0), shaftwidth= 1, color = color.white))
    axis.append(arrow(pos=vec(a,b,0), axis=vec(0,d+50,0), shaftwidth= 1, color = color.white))
    for t in range(0,20):
        axis.append(box(pos=vec(a + (t+1)*(c/20),b+d/2,0), length=1, height=d, width=1))
    
    for j in range(0,10):
        axis.append(box(pos=vec(a + c/2,b + (j+1)*(d/10),0), length=c, height=1, width=1,color=color.gray(0.8)))
    
    for x in range(0,6):
        num = str(x*(c/5))
        labels.append(label(pos=vec(a + x*(c/5),b-2*d/20,0), text = num, height = 20, border = 12, font = 'monospace', color = color.white, box = False))
    
    for y in range(0,6):
        num = str(b+y*(d/5))
        labels.append(label(pos=vec(a-2*c/40,b + y*(d/5),0), text = num, height = 20, border = 12, font = 'monospace', color = color.white, box = False))

def projectileInit():
    global scene, init_value_box, ball_pos_box, height, angle, touch_box, jBox
    scene = display(width=800, height=600,forward=vec(0.5,-0.05,-1), background=vec(0.6,0.3,0.2),center =vec (200,100,0), range = 250)
    floor = box(length=500,height=0.5,width=250,pos = vec(250,0,0),color=vec(0,1,0))  
    ball_pos_box = label(pos=vec(400,300,0), text= 'Position:\nX:' + '\nY:' + '\nZ:', height=20, border=10, font='monospace', color = color.white)
    axisInit()
    init_value_box = label(pos=vec(200,300,0), text= 'Initial values:\n' + 'Angle:' + '\nHeight:'+ '\nSpeed:', height=20, border=10, font='monospace', color = color.white)   
    jBox = label(pos=vec(5,3,88), text= 'Touch  '  , \
      height=16, border=2, font='monospace', color = color.white)  
    touch_box = label(pos=vec(5,233,0), text= 'Touch at\n  X = '  , \
      height=20, border=10, font='monospace', color = color.white)   
    
def projectile_motion(data): #odf = (height, included angle, speed)
    global is_running, ball_pos_box, ball_touch, height, angle, z
    speed = data[0]
    ball = sphere(pos = vec(0, height + 8.25, z), radius = 8, color = color.white)
    ball.velocity = vector(speed * cos(angle), speed * sin(angle), 0)
    g = 9.8 
    dt = 0.003
    ball_touch = 0
    frame_count = 0
    def jump(): 
        global is_running, frame_count, isExist, ball_touch, touch_box, jBox
        a = vector(0, -g, 0)
        ball.pos = ball.pos + ball.velocity * dt + 0.5 * a * (dt ** 2)
        if ball.pos.y < 8.25 and ball.velocity.y < 0:
            ball.velocity.y = - ball.velocity.y
            ball_touch += 1     
            playAudio('chord.wav') 
            touch_box.text= 'Touch at\n  X = ' + str(round(ball.pos.x,1))  
            jBox.text= 'Touch ' + str(round(ball_touch,1))  
            update()
            if ball_touch < 3 and ball.pos.x < 500 and  ball.pos.x >= 400:
                playAudio('gj.wav')   # Good Job if touch less than 2 times 
        else:
            ball.velocity = ball.velocity + a * dt
        if ball.pos.x > 500 or ball_touch >= 10:
            ball.visible = False
            is_running = False
            return
        else:
            rate(1000, jump)
        if frame_count % 10 == 0:
            ball_pos_box.text = 'Position:\nX:' + str(round(ball.pos.x,1)) + '\nY:' + str(round(ball.pos.y,1)) + '\nZ:' + str(ball.pos.z)
        frame_count += 1
    jump()
    


def Angle(data):
    if data != None:
        global angle
        angle = data[0]*2*Math.PI/360
        update()

def Speed(data):
    global is_running, speed, jBox
    if data != None:
        jBox.text= 'Touch 0' 
        speed = data[0]
        update() 
    if not is_running and (data != None) and (data[0] > 5):
        is_running = True
        update()
        projectile_motion([speed])
    
def Height(data):
    if data != None:
        global height
        height = data[0]
        update()

def setup():
    global init_value_box
    projectileInit()
    profile = {
        'dm_name': 'Ball-throw1',
        'odf_list': [Angle, Speed, Height],
    }

    dai(profile)  
    playAudio('Startup.wav')  

def update():
    global init_value_box, height, angle, get_data, speed
    init_value_box.text = 'Initial values:\n' + 'Angle:' + str(round(angle*180/3.14,1)) + '\nHeight:' + str(round(height,1)) + '\nSpeed:' + str(round(speed,1))
  
setup()

## ======= END of the file Ball-throw1.py    =========      ===============================

 

VPython Device Ball-throw1  on class.iottalk.twVPython Device Ball-throw2  on class.iottalk.tw

## 以下是搭配 Ball-throw1.py (其實可以任何 .py)的網頁 (用 View Source 檢視網頁)

***上述 Ball-throw1 是 IoTtalk 的 VPython DA, 其實也可以 .. 可以把 Ball-throw1.py 改一下放到 EduTalk3 上面 + 用手機 掃描 QR Code 後控制 *也可以把 Ball-throw2 稍微改一下丟入 EduTalk 的行星運動用手機控制
<!DOCTYPE html>
<html lang="en">
<head>
  <link rel="shortcut icon" href="/static/myicon.png?v=3'>
  <meta charset="UTF-8">
  <script src="https://class.iottalk.tw//da/vp/js/jquery.min.js"></script>
  <script src="https://class.iottalk.tw//da/vp/js/jquery.mousewheel.js"></script>
  <script src="https://class.iottalk.tw//da/vp/js/jquery-ui.custom.min.js"></script>
  <script src="https://class.iottalk.tw//da/vp/js/RScompiler.2.1.min.js"></script>
  <script src="https://class.iottalk.tw//da/vp/js/RSrun.2.1.min.js"></script>
  <script src="https://class.iottalk.tw//da/vp/js/compiler.2.1.min.js"></script>
  <script src="https://class.iottalk.tw//da/vp/js/glow.2.1.min.js"></script>
  <script src="https://class.iottalk.tw//da/vp/js/csmapi.js"></script>
  <script src="https://class.iottalk.tw//da/vp/js/dan.js"></script>
  <title>VPython</title>
</head>
<body> 
  <div id="glowscript" class="glowscript"></div>
<video  height=32 id="video" muted autoplay>So that play sound OK</video>
<button id="goButton"><font size=5>按這繼續 Ball-throw1</font></button>
<script>
   goButton.addEventListener('click', function() {
     video.muted = false;
     document.getElementById('goButton').style.visibility = 'hidden';
     var script = document.createElement('script'); 
        script.src =  "./dai.js"; 
        document.head.appendChild(script) 
   });
     
//  <script src="./dai.js"></script>
</script>

</body>
</html>

<!------------------------  
    * 不要用  <base href= ...  >    因為那會更麻煩喔 ! 
    *  因為那樣要用如 file:///C:/users/ ...  這種很長的寫法來指定本地機器上的檔案 !
------------------------------------------->
*** 詳細可以 點這看影片說明 (善用影片下方說明與子題連結)

==> 把上述 HTML 修改後存檔 假設叫 haha.html, 和 dai.js 放同目錄; 當然 dan.js 和 .py 也可放一起!
* 因為Java Script 和 Javascript Library 可在任何地方, 沒有跨域請求(CORS)問題!
    所以, 我們大部分只要讓 HTML 去抓 class.iottalk 上的 JS code 即可;
     ( 好用的招數     <base href="https://class.iottalk.tw">  )
    原則上只改 dai.js 那列, 讓它從我們磁碟目錄中讀取 dai.js 例如: 
      <script src="./dai.js"></script> 
 ==> 記得把 dai.js 抓回自己電腦先!
     另外, 因為 Chrome 不允許跨網域來的影音自動播放,
     所以為了可以在 VPython 裡面播放聲音, 上述 HTML 改為:
     要讓 User 互動按個按鈕再載入 dai.js
如果想改 d_name 則也要把 dan.js 改為在自己電腦上; 因為 d_name 被寫在 dan.js 用亂數決定!
      <script src="./dan.js"></script> 
  *** 注意, dan.js 必須比 dai.js 先被 Load 進來 ! 因為 dai.js 會用到 dan.js 的冬冬 !
    *** 如果 dan.js 有寫成 d_name 沒設定才用亂數決定,
           則我們就可以在 dai.js 內用類似如下設定: 
                dan.profile[d_name] = "我的 d_name"  

** 假設最前面那個 VPython 的 .py 檔案也放目前的目錄
** 修改 dai.js : 確認要連接之 IoTtalk server, 要抓的 VPython .py 檔案
   LINE 2 改為:
        var project = "Ball-throw1";  // 其實這沒用了 (本來搭配 LINE 132)
   LINE 5 改為:
        csmapi.set_endpoint ("https://class.iottalk.tw");   // 要連的 server
      ==>做這練習題可使用任何可以用的 IoTtalk  Server
       ( 原先 code:   csmapi.set_endpoint (window.location.origin);   表示與該網頁同一個機器 )
   LINE 89 改為: (或把該些 .wav 檔案也偷回自己電腦 :-)
      audio[filename] = new Audio('https://class.iottalk.tw/da/vp/audio/' + filename);
 
   LINE 132 改為:(抓本地的 python code; 或自己網站的該檔案之 URL)
       fetch_code('./Ball-throw1.py');     // VPython 檔案名稱, 我偷懶則用 b1.py
      ==> 如果有弄自己的網站, 則改用 http:// 從自己網站的檔案 fetch (抓取) 
在 CMD 命令視窗中, 打入 haha.html 按 ENTER,
   發現不會執行 (空白網頁); 
   這目的是方便在網址列取得 haha.html 的路徑!

* 在 CMD 命令視窗中, 開啟不要管檔案安全性的 Chrome 瀏覽器:
 start chrome --user-data-dir="." --disable-web-security
* 把剛才看起來空白頁的網址複製到這新開的瀏覽器!

*** 詳細可以 點這看影片說明(善用影片下方說明與子題連結)

Top  幾個英文字讀音
* 請注意目前 IoTtalk 伺服器 上的 Free-Fall2 只接受一個參數
*也可以把 Ball-throw2 稍微改一下丟入 EduTalk 的行星運動用手機控制** EduTalk 總部在交大 (好像掛掉了:-(       **台大石明豐教授 Vpython+Physics 課程文件(pdf,5.28MB)
  ** 補充說明:
      在 IoTtalk 系統首頁 VPython List 之下的視覺化網頁應用(Visual Web Application)
      都是用 VPython 視覺化的語言(其實是程式庫)寫的應用 !
      在 VPython 的首頁 就說:
      VPython 是 3D Programming for Ordinary Mortals (凡人用的 3D 程式設計)

      * 想體驗 VPython programming,
          請點這連到 https://EduTalk3.NCTU.edu.tw/ (免費註冊 Sign up)

                  (如剛好網站掛了, 把 3 改為 1 用 edutalk1 也可)

✨如果上述 EDUTalk 不能用,可以改用這 pasedu.iottalk.tw/edutalk 但是,修改方法不太一樣 !
  (1)註冊, 登入
  (2)點 行星運動
  (3) 點 Program 可看到程式碼
  (4)點 Animation 秀出執行畫面
  (5)點 Animation 右邊那個 QR code 產生鈕
  (6)拿出你手機, 確定網路有通, 叫出 QR code 掃描功能掃畫面上 QR code
  (7)關掉螢幕上 QR code 子畫面免得礙眼!
  (8)用你手機控制重力 和 速度
然後.. 可試著修改程式碼, 甚至整個換掉 ..只要是需要輸入兩個值來控制的都可以 ! (dm_nam 和 df_list 設相同)
      * 想看更多 VPython programming 範例,
          請點這連到 VPython 官方的 Examples

    VPythonPython中一個稱為 Visual 的模組所擴充而來。
其原始架構是由Carnegie Mellon University的大學生 David Scherer 在2000年開發出來,核心為Python 這個易學的程式語言,以其建構的三維繪圖模組來描述虛擬的物理世界,讓使用者可以輕易的利用程式來做三維空間的物理繪圖,具備有動態的時間演進和不同的視角觀察,讓學生可以更直覺的利用程式來學習物理,尤其是需要視覺化和動態觀察的物理現象。

    VPython 目前有多個版本被廣泛使用,除了原本的單機版的 VPython 之外,另有在網頁上可執行的GlowScript,與新世代的vpython-jupyter計畫(就是提供 API 讓 VPython 可以在 網頁執行。

    在 IoTtalk 系統裡面的 VPython List 各應用 以及在 EduTalk3.NCTU.edu.tw 都是在瀏覽器使用 GlowScript 程式庫, 執行 VPython 程式碼)。


✨如果上述 EDUTalk 不能用,可以改用這 pasedu.iottalk.tw/edutalk 但是,修改方法不太一樣 !  

       
** 把 Ball-throw1.py 放到 EduTalk3 上面 + 用手機掃描QRCode之後控制
      (EduTalk4 因為南港高中高慧君老師太認真似乎把它改壞了 :-)[更新:現應該已經修復]
EduTalk : IoTtalk與3D互動式程式設計 (Edutalk3 的 3 也可改為 1, 2, 4, 或 6 連到不同機器)
✨如果上述 EDUTalk 不能用,可以改用這 pasedu.iottalk.tw/edutalk
      但是,修改方法不太一樣 !

## Ball-throw1.py 用來在 https://edutalk3.nctu.edu.tw/  替換 行星運動 的 code
#  貌似 edutalk3, edutalk5, .. 該六部 EduTalk 都掛掉沒人維護了:-(   
# 那請點這看改用 pasedu 的 /edutalk 要如何修改
axis = []
labels = []
is_running = False
speed = 0
angle = 45*2*3.14/360 
height = 58
z = 68
preloadAudio('Startup.wav')
preloadAudio('chord.wav')
preloadAudio('gj.wav')

def axisInit():
    global axis, labels
    a = 0
    b = 0
    c = 500
    d = 200
    axis.append(arrow(pos=vec(a,b,0), axis=vec(c+50,0,0), shaftwidth= 1, color = color.white))
    axis.append(arrow(pos=vec(a,b,0), axis=vec(0,d+50,0), shaftwidth= 1, color = color.white))
    for t in range(0,20):
        axis.append(box(pos=vec(a + (t+1)*(c/20),b+d/2,0), length=1, height=d, width=1))
    
    for j in range(0,10):
        axis.append(box(pos=vec(a + c/2,b + (j+1)*(d/10),0), length=c, height=1, width=1,color=color.gray(0.8)))
    
    for x in range(0,6):
        num = str(x*(c/5))
        labels.append(label(pos=vec(a + x*(c/5),b-2*d/20,0), text = num, height = 20, border = 12, font = 'monospace', color = color.white, box = False))
    
    for y in range(0,6):
        num = str(b+y*(d/5))
        labels.append(label(pos=vec(a-2*c/40,b + y*(d/5),0), text = num, height = 20, border = 12, font = 'monospace', color = color.white, box = False))

def projectileInit():
    global scene, init_value_box, ball_pos_box, height, angle, touch_box, jBox
    scene = display(width=800, height=600,forward=vec(0.5,-0.05,-1), background=vec(0.6,0.3,0.2),center =vec (200,100,0), range = 250)
    floor = box(length=500,height=0.5,width=250,pos = vec(250,0,0),color=vec(0,1,0))  
    ball_pos_box = label(pos=vec(400,300,0), text= 'Position:\nX:' + '\nY:' + '\nZ:', height=20, border=10, font='monospace', color = color.white)
    axisInit()
    init_value_box = label(pos=vec(200,300,0), text= 'Initial values:\n' + 'Angle:' + '\nHeight:'+ '\nSpeed:', height=20, border=10, font='monospace', color = color.white)
    jBox = label(pos=vec(5,3,88), text= 'Touch  '  , \
      height=16, border=2, font='monospace', color = color.white)  
    touch_box = label(pos=vec(5,233,0), text= 'Touch at\n  X = '  , \
      height=20, border=10, font='monospace', color = color.white)  
    
def projectile_motion(data): #odf = (height, included angle, speed)
    global is_running, ball_pos_box, ball_touch, height, angle, z
    speed = data[0]
    ball = sphere(pos = vec(0, height + 8.25, z), radius = 8, color = color.white)
    ball.velocity = vector(speed * cos(angle), speed * sin(angle), 0)
    g = 9.8 
    dt = 0.003
    ball_touch = 0
    frame_count = 0
    def jump(): 
        global is_running, frame_count, isExist, ball_touch, touch_box, jBox 
        a = vector(0, -g, 0)
        ball.pos = ball.pos + ball.velocity * dt + 0.5 * a * (dt ** 2)
        if ball.pos.y < 8.25 and ball.velocity.y < 0:
            ball.velocity.y = - ball.velocity.y
            ball_touch += 1     
            playAudio('chord.wav') 
            jBox.text= 'Touch ' + str(round(ball_touch,1))  
            touch_box.text= 'Touch at\n  X = ' + str(round(ball.pos.x,1))  
            if ball_touch < 3 and ball.pos.x < 500 and  ball.pos.x >= 400:
                playAudio('gj.wav')   # Good Job if touch less than 2 times  
        else:
            ball.velocity = ball.velocity + a * dt
        if ball.pos.x > 500 or ball_touch >= 10:
            ball.visible = False
            is_running = False
            return
        else:
            rate(1000, jump)
        if frame_count % 10 == 0:
            ball_pos_box.text = 'Position:\nX:' + str(round(ball.pos.x,1)) + '\nY:' + str(round(ball.pos.y,1)) + '\nZ:' + str(ball.pos.z)
        frame_count += 1
    jump()

import random
def Gravity(data):
    if data != None:
        global angle, height
        angle = data[0]*2*10*Math.PI/360
        height = 2 + data[0]* 20 - 88 * random.random()
        if height < 0:
            height = 0
        update()  

def Speed(data):
    global is_running, speed, jBox
    jBox.text= 'Touch 0' 
    speed = data[0]
    update() 
    if not is_running and (data != None) and (data[0] > 5):
        is_running = True 
        speed *= 2 
        update()
        projectile_motion([speed])
    
def Height(data):
    if data != None:
        global height
        height = data[0]
        update()

def setup():
    global init_value_box
    projectileInit()
    profile = {   
        'dm_name': 'Universe',
        'df_list': [Gravity, Speed],      # 注意這用 df_list 跟 IoTtalk 不同
    }

    dai(profile)  
    playAudio('Startup.wav')  
    update()

def update():
    global init_value_box, height, angle, get_data, speed
    init_value_box.text = 'Initial values:\n' + \
     'Angle:' + str(round(angle*180/3.14,1)) + '\nHeight:' + str(round(height,1)) + \
     '\nSpeed:' + str(round(speed,1))
  
setup()

## ======= END of the file Ball-throw1 used with https://edutalk3.nctu.edu.tw/

 
🅱️ 🅱️ 🅱️ 🅱️ 🅱️ 🅱️ 🅱️ 🅱️
## Ball-throw1.py used with https://pasedu.iottalk.tw/edutalk/
##以下是把 IoTTalk 上的 Ball-throw1.py 改為配合 pasedu EDUTALK 上的行星運動。
Gravity_I = 0

Speed_I = 0

# 以上變數讀取感測器後會自動更新
# 請勿修改上方程式碼
#
## Ball-throw1.py 用來在 https://pasedu.iottalk.tw/edutalk/ 替換 行星運動 的 code
# 注意 IoTtalk 上的 Ball-throw1.py 是需要三個參數的,
##  但是 EduTalk 的 Universe 行星運動只有兩個參數, 所以 "高度" 就不直接用參數啦!

# 模擬實驗參數區
freq = 120        # 更新頻率(Hz)
dt = 1.0 / freq   # 更新間隔(second)

# 事件旗標區
reset_flag = False
axis = []
labels = []
is_running = False
speed = 58
angle = 45*2*3.14/360 
height = 68
z = 68
preloadAudio('Startup.wav')
preloadAudio('chord.wav')
preloadAudio('gj.wav')

def axisInit():
    global axis, labels
    a = 0
    b = 0
    c = 500
    d = 200
    axis.append(arrow(pos=vec(a,b,0), axis=vec(c+50,0,0), shaftwidth= 1, color = color.white))
    axis.append(arrow(pos=vec(a,b,0), axis=vec(0,d+50,0), shaftwidth= 1, color = color.white))
    for t in range(0,20):
        axis.append(box(pos=vec(a + (t+1)*(c/20),b+d/2,0), length=1, height=d, width=1))
    
    for j in range(0,10):
        axis.append(box(pos=vec(a + c/2,b + (j+1)*(d/10),0), length=c, height=1,\
           width=1,color=color.gray(0.8)))
     
    for x in range(0,6):
        num = str(x*(c/5))
        labels.append(label(pos=vec(a + x*(c/5),b-2*d/20,0), text = num, height = 20,\
            border = 12, font = 'monospace', color = color.white, box = False))
    
    for y in range(0,6):
        num = str(b+y*(d/5))
        labels.append(label(pos=vec(a-2*c/40,b + y*(d/5),0), text = num, height = 20, \
           border = 12, font = 'monospace', color = color.white, box = False))

def projectileInit():
    global scene, init_value_box, ball_pos_box, height, angle, touch_box, jBox
    scene = display(width=800, height=600,forward=vec(0.5,-0.05,-1),\
       background=vec(0.6,0.3,0.2),center =vec (200,100,0), range = 250)
    floor = box(length=500,height=0.5,width=250,pos = vec(250,0,0),color=vec(0,1,0))  
    ball_pos_box = label(pos=vec(400,300,0), \
       text= 'Position:\nX:' + '\nY:' + '\nZ:', height=20, border=10,\
       font='monospace', color = color.white)
    axisInit()
    init_value_box = label(pos=vec(200,300,0), text= 'Initial values:\n' + 'Angle:' + \
      '\nHeight:'+ '\nSpeed:', height=20, border=10, font='monospace', color = color.white)
    jBox = label(pos=vec(5,3,88), text= 'Touch  '  , \
      height=16, border=2, font='monospace', color = color.white)  
    touch_box = label(pos=vec(5,233,0), text= 'Touch at\n  X = '  , \
      height=20, border=10, font='monospace', color = color.white)  
    
def projectile_motion(data): #odf = (height, included angle, speed)
    global is_running, ball_pos_box, ball_touch, height, angle, z
    speed = data[0]
    ball = sphere(pos = vec(0, height + 8.25, z), radius = 8, color = color.white)
    ball.velocity = vector(speed * cos(angle), speed * sin(angle), 0)
    g = 9.8 
    dt = 0.003
    ball_touch = 0
    frame_count = 0
    def jump(): 
        global is_running, frame_count, isExist, ball_touch, touch_box, jBox 
        a = vector(0, -g, 0)
        ball.pos = ball.pos + ball.velocity * dt + 0.5 * a * (dt ** 2)
        if ball.pos.y < 8.25 and ball.velocity.y < 0:
            ball.velocity.y = - ball.velocity.y
            ball_touch += 1     
            playAudio('chord.wav') 
            jBox.text= 'Touch ' + str(round(ball_touch,1))  
            touch_box.text= 'Touch at\n  X = ' + str(round(ball.pos.x,1))  
            if ball_touch < 3 and ball.pos.x < 500 and  ball.pos.x >= 400:
                playAudio('gj.wav')   # Good Job if touch less than 2 times  
        else:
            ball.velocity = ball.velocity + a * dt
        if ball.pos.x > 500 or ball_touch >= 10:
            ball.visible = False
            is_running = False
            return
        else:
            rate(1000, jump)
        if ball.pos.x < -10 :
            ball.visible = False
            is_running = False
            return
        if frame_count % 10 == 0:
            ball_pos_box.text = 'Position:\nX:' + str(round(ball.pos.x,1)) + '\nY:' + \
             str(round(ball.pos.y,1)) + '\nZ:' + str(ball.pos.z)
        frame_count += 1
    jump()

    
import random
def Gravity(data):
    if data != None:
        global angle, height, speed
        angle = data[0]*2*10*Math.PI/360
        height = 2 + data[0]* 20 - 88 * random.random()
        if height < 15:
            height = 15
        update()  

def Speed(data):
    global is_running, speed, jBox
    jBox.text= 'Touch 0' 
    speed = data[0] 
    speed *= 10
    update() 
    if not is_running and (data != None) and (data[0] > 1.5):
        is_running = True 
        #speed *= 2 
        update()
        projectile_motion([speed])
    
def Height(data):
    if data != None:
        global height
        height = data[0]
        update()

def setup():
    global angle, height, speed
    global init_value_box
    playAudio('Startup.wav')  
    projectileInit()

def update():
    global init_value_box, height, angle, get_data, speed
    init_value_box.text = 'Initial values:\n' + \
     'Angle:' + str(round(angle*180/3.14,1)) + '\nHeight:' + str(round(height,1)) + \
     '\nSpeed:' + str(round(speed,1))

setup()

#用來判斷萬有引力常數、速度是否改變
prev_state = (Gravity_I, Speed_I)

while True:
    rate(freq)
    update( )
    if prev_state != (Gravity_I, Speed_I):
       Gravity( [Gravity_I] )
       Speed( [Speed_I] )
       prev_state = (Gravity_I, Speed_I)
    
## ======= END of the file Ball-throw1 used with https://pasedu.iottalk.tw/edutalk/

* 請注意目前 IoTtalk 伺服器 上的 Free-Fall2 只接受一個參數
** 也可以把 Ball-throw2.py 改一下丟入 Edutalk 的行星運動用手機控制
** EduTalk 總部在交大 (好像掛掉了)       **台大石明豐教授 Vpython+Physics 課程文件(pdf,5.28MB)
EduTalk : IoTtalk與3D互動式程式設計 (Edutalk3 的 3 也可改為 1, 2, 4, 或 6 連到不同機器)
  (1)點上述網站進入後, 註冊, 登入
  (2)點 行星運動
  (3) 點 Program 可看到程式碼
  (4)點 Animation 秀出執行畫面
  (5)點 Animation 右邊那個 QR code 產生鈕
  (6)拿出你手機, 確定網路有通, 叫出 QR code 掃描功能掃畫面上 QR code
  (7)關掉螢幕上 QR code 子畫面免得礙眼!
  (8)用你手機控制重力 和 速度
   -------------------
  (9)點 Program, 把程式碼砍掉: 在程式碼窗點一下敲 CTRL_A 然後按 DEL 鍵
  (10)把上面的 Ball-throw1.py 程式碼複製貼到 剛砍掉的程式碼窗內
  (11)點 Animation 秀出執行畫面
  (12)用手機繼續控制畫面;
  (13)必要時重新做 (5)和(6)
  (14)除了用手機掃描 QRcode 弄出遙控器,也可從電腦叫出遙控器網頁
    (a)在  Animation 秀出執行畫面後,按 F12 或滑鼠右鍵選檢查 Inspect 進入開發者模式
    (b)點 Console 主控台然後滾輪往前捲到最前面可看到遙控器網址
       該網址其實就是 QRcode 顯示的,點它開啟遙控器網頁
    (c)把遙控器網頁拖下到右邊弄小
    (d)回 Animation 畫面後,按 F12 離開開發者模式
    (e)讓遙控器網頁在不遮住 Animation 畫面狀況下拉動兩個 Slider 看看

* 以下是利用 Flask + Python 在自己電腦跑 python w80.py 建立了網站; 然後用 127.0.0.1/abc 啟動
* 這 Ball-throw1.py 已經參考 Ball-throw2.py 加入了聲音(包括啟動, 觸地, 好球)
* 按滑鼠右鍵可以調整到喜歡的角度觀看 (P.S. 我把 Ball-Throw1.py 的球的 z 改為 68 方便觀看。)
   動手做 !  動手做 !!  動手做 !!! 

** class.iottalk.tw

** pasedu.iottalk.tw/edutalk/「分享」按鈕- 社交外掛程式 - Facebook for Developers

請按  幫忙分享喔 !
有時候看起來對的其實是錯的!(我很久以前寫的網誌:-) * 不能反攻大陸的真正理由!(也是以前寫的, 其實是給大一學生的習題:-) * 何以1752年9月只有19天 ? 參考 Unix 的 cal 命令 ..(也是大一習題:-) * 為什麼中國(清朝)的 1911年 12 月只有 18 天 ?!(還是以前寫的:-) * [茶餘飯後] 魑魅魍魎 鬼話連篇(大一計概介紹802.11無線網路:-) * [很小的數]何處惹塵埃? 沙漠中是塵埃!(大一計概:-) * [很大的數]再談關於 Google 與 Googol(大一計概:-) * [也是很大的數]佛曰不可說不可說 比一個 googol 還大!(大一計概:-) * 物聯網、IoTtalk、薑黃、免疫力...(兩個月前寫的:-) * 請注意..感冒其實沒有藥     大家要顧好身體, 否則沒機會實現你的創意!我也有粉絲專頁ㄟ:-) 呵呵 (據說 "呵呵" 是北宋蘇東坡的口頭禪 :-)
你不懂的內容農場取妻當娶李子柒? 7 min.   ( 館長也哈她  ) * 台灣百大 Youtuber 收入排行全球10大最高收入华人YouTuber, 2019.1203, by 小楠ngrok 讓外部能夠連到 Intranet 內 Localhost的網站及服務Facebook Messenger Bot 範例 https://developers.facebook.com/docs/messenger-platform/getting-started/sample-apps/?translation用Python 寫一個 fb Messenger Bot手把手教你搭建 LINE 聊天機器人LineBot+Python,輕鬆建立聊天機器人(北科大程式設計研究社) * https://developers.line.biz/en/services/messaging-api/(LINE 關方網站教學文件) * 防詐騙、防詐騙、防詐騙 南寧詐騙案全台灣有 25萬人被騙 !小心詐騙 !小心詐騙 !小心詐騙 !投資自己,讓今天的自己 比 昨天的自己 更值錢 ! ( 建議每天 15分鐘在 voiceTube.tw ) *
 
** EduTalk : IoTtalk與3D互動式程式設計 (Edutalk3 的 3 也可改為 1, 2, 4, 或 6 連到不同機器)
  ( 使用方法參看前面搭配 EduTalk 的 Ball-throw1.py )
## Ball-throw2.py  用來搭配 https://edutalk3.nctu.edu.tw/  替換 行星運動 的 program code
## 似乎目前 edutalk3, edutalk5, .. 這六部 EduTalk server 都掛掉沒人維護啦 :-(
##  阿那個, 你可以改用 pasedu.iottalk.tw/edutalk/ 但 .py 程式碼改法大不同
g=9.8
size = 0.7
scene = display(width=600, height=500, background=vector(0.6,0.3,0.2), center=vector(-7,7,0))
floor = box(length=24, height=0.5, width=4, color=color.green, pos=vector(-9,0,0))
box(length=8, height=0.5, width=4, color=color.red, pos= vector(7,0,0) )
box(length=8, height=0.5, width=4, color=color.green, pos= vector(15,0,0) )
jBox = label(pos=vec(3,15,0), text= 'Radius:  '  , \
      height=16, border=2, font='monospace', color = color.white)  

scene.range=16
camera_x1=0.8
camera_x2=0.0
scene.forward=vector(camera_x1,camera_x2,-1)

preloadAudio('Startup.wav')
preloadAudio('chord.wav')
preloadAudio('gj.wav')


def balljump(spd):
    if (spd < 3): return 

    ball = sphere(pos=vector(-24.5, 10.0, 0.0), radius=size, color=color.white)
    ball.velocity = vector(spd, -1.0, 0.0)

    dt = 0.003
    gj_playFlag=1

    def resetScene(): 
        global jBox, size
        scene.background = vector(0.6, 0.3, 0.2) 
        jBox.text= 'Radius: ' + str(size) 
        
    
    def jump():
        global gj_playFlag, size

        #console.log(ball.pos.x, ball.pos.y)
        if ball.pos.x < 15:
          rate(1000, jump)
        else:
          ball.visible = False
          gj_playFlag = 1 
          return

        if (ball.pos.x < 10 and ball.pos.x > 1.5) and ball.pos.y < size*1.5 :
            scene.background = vector(0,0,0)
            if gj_playFlag: 
                playAudio('gj.wav') 
                gj_playFlag=0
            rate(4, resetScene)
		
        previous_x = ball.pos.x
        ball.pos = ball.pos + ball.velocity * dt

        if ball.pos.y < size and ball.velocity.y < 0:
            ball.velocity.y = - ball.velocity.y
            playAudio('chord.wav')
        else:
            ball.velocity.y = ball.velocity.y - g * dt
    
    jump()

def Gravity(data):
    global size, jBox
    if data != None:
        size = data[0] / 2
        if size < 0.25:
            size=0.25
        size = int(size*1000)/1000.0   # 小數點以後三位
        jBox.text= 'Radius: ' + str(size) 

def Speed(data):
    if data != None:
        balljump(data[0])

def setup(): 
    global jBox, size
    profile = {  
        'dm_name': 'Universe',
        'df_list': [Gravity, Speed],  
    }
    jBox.text= 'Radius: ' + str(size)  
    dai(profile)
    playAudio('Startup.wav')

setup()

##  throw2 ======= END of the file Ball-throw2.py 

### Ball-throw2.py  用來搭配 https://edutalk3.nctu.edu.tw/  替換 行星運動 的 program code
 
## Ball-throw2.py 改為 搭配 https://pasedu.iottalk.tw/edutalk/
## Ball-throw2.py 改為 搭配 pasedu EDUTALK /edutalk 
Gravity_I = 5.8
# 其實 Ball-throw2 只有一個 ODF; 這是要配合 EduTalk 的行星運動 Universe
Speed_I = 0
# 以上變數讀取感測器後會自動更新; Gravity_I 拿來控制球的半徑 :-)
# 請勿修改上方程式碼
freq = 60
dt = 1/freq
###
## Ball-throw2.py  用來搭配 https://pasedu.iottalk.tw/edutalk/ 替換 行星運動 的 program code
g=9.8
size = 0.7
speed = 8
scene = display(width=600, height=500, background=vector(0.6,0.3,0.2), center=vector(-7,7,0))
floor = box(length=24, height=0.5, width=4, color=color.green, pos=vector(-9,0,0))
box(length=8, height=0.5, width=4, color=color.red, pos= vector(7,0,0) )
box(length=8, height=0.5, width=4, color=color.green, pos= vector(15,0,0) )
jBox = label(pos=vec(3,15,0), text= 'Radius:  '  , \
      height=16, border=2, font='monospace', color = color.white)  

scene.range=16
camera_x1=0.8
camera_x2=0.0
scene.forward=vector(camera_x1,camera_x2,-1)

preloadAudio('Startup.wav')
preloadAudio('chord.wav')
preloadAudio('gj.wav')

def balljump(spd):
    global speed
    if (spd < 3): return 
    speed = spd

    ball = sphere(pos=vector(-24.5, 10.0, 0.0), radius=size, color=color.white)
    ball.velocity = vector(spd, -1.0, 0.0)

    dt = 0.003
    gj_playFlag=1

    def resetScene(): 
        global jBox, size
        scene.background = vector(0.6, 0.3, 0.2) 
        jBox.text= 'Radius: ' + str(size) + "\nSpeed: " + str(speed)

    def jump():
        global gj_playFlag, size
        #console.log(ball.pos.x, ball.pos.y)
        if ball.pos.x < 15:
          rate(1000, jump)
        else:
          ball.visible = False
          gj_playFlag = 1 
          return

        if (ball.pos.x < 10 and ball.pos.x > 1.5) and ball.pos.y < size*1.5 :
            scene.background = vector(0,0,0)
            if gj_playFlag: 
                playAudio('gj.wav') 
                gj_playFlag=0
            rate(4, resetScene)
		
        previous_x = ball.pos.x
        ball.pos = ball.pos + ball.velocity * dt

        if ball.pos.y < size and ball.velocity.y < 0:
            ball.velocity.y = - ball.velocity.y
            playAudio('chord.wav')
        else:
            ball.velocity.y = ball.velocity.y - g * dt
    
    jump()

def Gravity(data):
    global size, jBox
    if data != None:
        size = data[0] 
        size = int(size*100)/100.0   # 小數點以後 2 位
        if size < 0.25:
            size=0.25
        jBox.text= 'Radius: ' + str(size) + "\nSpeed: " + str(speed)

def Speed(data):
    global speed
    if data != None:
        speed = data[0]*10
        speed = int(speed*100)/100.0
        balljump(speed)

def setup(): 
    global jBox, size
    jBox.text= 'Radius: ' + str(size) 
    #dai(profile)
    playAudio('Startup.wav')

def update():
    global height, angle, get_data, speed
    global size, jBox
    jBox.text= 'Radius: ' + str(size) + "\nSpeed: " + str(speed)

setup()

#用來判斷萬有引力常數、速度是否改變
prev_state = (Gravity_I, Speed_I)

while True:
    rate(freq)
    update( )
    if prev_state != (Gravity_I, Speed_I):
       prev_state = (Gravity_I, Speed_I)
       Gravity( [Gravity_I] )
       Speed( [Speed_I] )

##  pasedu EduTalk --- Ball-throw2 用來搭配 pasedu EDUTALK /edutalk
## ╰┈➤ https://pasedu.iottalk.tw/edutalk/

** * EduTalk : IoTtalk與3D互動式程式設計
* 提醒 IoTtalk 新手入門應用手冊在這: https://iottalk.vip/000/;
* 關於 IoTtalk 的使用也請先大概看看IoTtalk的使用手冊:
    http://liny.cs.nctu.edu.tw/#IoTtalk(林一平教授網頁) 
  ==> 點入後在 Document 下方, 有中文版和英文版;   也可點這看更多手冊
 * 如想用NodeMCU-ESP8266連接 IoTtalk, 建議也 先看看 ArduTalk 操作手冊 
      以及如何使用NodeMCU/Arduino Yun 控制 Device? (會在這頁往下跳)
 * 關於 IoTtalk System 的相關術語(Terminology)(看完後可以比較容易看懂 IoTtalk 相關的程式碼)
**點這跳到往下跳再看一下 IoTtalk 物聯網平台簡介 (在入門手冊也有)
**點這跳到看如何增加/修改 Device Model 物聯網設備類別 ?(例如 Rabboni)
**關於 網頁如何和 Python flask 程式之間互傳資料 ? (Q&A 2)  
*IoTtalk 簡介 + Python 簡介
** 要測試使用IoTtalk 可以連這: https://class.iottalk.tw
      ( 備用機器: https://test.iottalk.tw )
(1)關於 IoTtalk 簡介可參考我這教學影片:
    https://youtu.be/yFjxBuISDOs (0317-Part 1)
    * 包括用遙控器控制開關燈泡, 點這從 21分23秒處開始播放
(2)重新錄製關於初步使用 IoTtalk -- 用遙控器控制燈泡
            (12分鐘, 聲音較大 別被嚇到:-)

      https://youtu.be/ptupVe_qnkY   ( 聲音較大!聲音較大!聲音較大!)

* 建議在 Youtube 內看, 建議設定 1080P 全螢幕

(3)關於用遙控器丟球 或 用手機丟球:
    * https://youtu.be/W8x-9X8DDGM&t=1475
(4)測試 project 時萬一有問題,要如何找到問題以及解決問題 (影片27:55處開始)
(5)(注意這仍較小聲)進階使用 -- 用新式遙控器控制燈泡顏色 + 用 Knob 控制燈泡顏色:
      https://youtu.be/nG_zWQBLYFQ
(6)更多關於 IoTtalk 的使用(把 Bulb 弄到自己電腦, 用 Python 寫 Dummy Device 程式):
      請用youtube 搜尋 iottalk + 0324
    ==> * 含 0324 P4 -- IoTtalk平台介紹與使用(二) - Part 4/5 Python 簡介
**關於 NodeMCU-ESP8266 與 IoTtalk 連結應用展示請參看: (有比較大聲一些些, 請注意調整音量)
  (1) https://youtu.be/2TcdsVSiLP4 簡介NodeMCU, 一分鐘
  (2) https://youtu.be/36NR2D90Gh0 使用Arduino IDE Blink範例, 五分鐘
  (3) https://youtu.be/TSglOkFO3oA 使用Arduino IDE Fade範例呼吸燈, 六分鐘
  (4) https://youtu.be/vXE_S6wCiVE 同(3), 但改外接 LED燈,  四分鐘
  (5) https://youtu.be/-Tr5pSL845I 讓 NodeMCU 透過手機連上網路, 六分鐘
  (6) https://youtu.be/kwqV_APzhLw 展示如何讓NodeMCU與IoTtalk平台雙向溝通, 14分鐘

** Python + Rabboni + 玩更多的 IoTtalk 應用
** 在電腦上用 Python 寫程式, 騙 IoTtalk 說我們是 "Smartphone",
   抓取 Rabboni 的六軸趕測器資料(用 USB 或藍芽皆可), 送去給 IoTtalk 系統 ..
 (1)先點這開啟 iottalk.vip/000/ 網頁 並 跳到 (十)Rabboni 小玩具
 (2)抓 RabboniTsaiwn.zip 回自己電腦修改並測試用 Rabboni 代替手機玩一些 IoTtalk 應用小遊戲
    在該(十)Rabboni 小玩具 項目下有 "Rabboni Rabboni Rabboni",
   => 點了會跳到我放雲端關於 Rabboni 的資料與可測試的程式 RabboniTsaiwn.zip
   => 把該 RabboniTsaiwn.zip 檔案抓回自己電腦, 解壓縮, 稍微修改(通常改兩列即可), 執行!
*** 參看Rabboni 假裝 Smartphone + IoTtalk 教學影片: https://youtu.be/-PXQDZX-EHk?t=2198 (善用 youtube 內影片下方的說明與子題目時間點連結)
** Windows + Python 操作不熟的建議如下操作: (a) 開啟 CMD 命令窗 打入這命令檢查已安裝的 python 版本: (必要時到 python.org 去抓來安裝) python --version 必須 3.7.3 (含)以上版本搭配 rabboni 程式庫才沒問題, 否則須抓 0.7 版程式庫 ! (b) 立即建立 Python 的虛擬環境(其實, 應該叫 隔離環境 才對!): python -m venv ggyy ggyy\Scripts\activate * 說明: 以上會讓 Python 建立一個子目錄 ggyy, 把必要的 Python 要用的 程式複製進去該子目錄, 然後那個 activate 是記住目前命令路徑並立即修改命令路徑! --- 等以後不用這環境時, 打 deactivate 敲 ENTER 鍵即可恢復原命令路徑! --- 當然之後要用這環境時, 再次執行剛剛的 activate 即可! *** 所以, 如果以前已經建立過隔離環境, 想辦法執行它的 activate 即可, 不必重新進立: 就是先用 cd 切換到看得到 ggyy 目錄, 然後打: ggyy\Scripts\activate ==> 不建議 cd 進入 ggyy 目錄, 如進入 ggyy 目錄內可打 Scripts\activate , 然後建議用 cd .. 回上一層目錄; 儘量不要在該目錄內做事! (c)現在回到剛開啟 CMD 窗之時的目錄 (通常是使用者的預設 Home 家目錄) ==> 從 CMD 窗建立要工作的目錄, 例如 test543 mkdir test543 (d)跳入 該 test543 目錄 ( cd 代表 change directory) cd test543 (e)用滑鼠想辦法開啟到看得到 test543 的資料夾 (就是你的 Home Directory) (f)讓 test543 這目錄變你的快速存取列目錄 (在 Windows 7 稱作 我的最愛) 用滑鼠拖拉即可, 如果是 Windows 10 可在 test543 按滑鼠右鍵選"釘選到快速存取列" (g)之後, 任何時候要用滑鼠點入 test543 目錄都很快 ! 在沒開啟任何資料夾時, 在最底下工作列有一個檔案夾可以點; 只要有開啟至少一個檔案夾, 左邊上半部就是快速存取列(Windows 7 稱作 我的最愛) ==> 用滑鼠右鍵點按檔案選喜歡的文字編輯器開啟檔案修改, 例如 IDLE 或 Notepad++ ==> 每次改好檔案敲 CTRL_S 存檔,        然後用 CMD 窗, 必要時用 cd 命令切換目錄, 打 python 檔案名稱.py 執行, 例如: python DAI.py ==> 注意, Python 的版本要 3.7.3, 3.7.5, 3.7.6, 3.7.7 這幾個, 不要用 Python 3.8.x *** 如果系統說缺啥, 就用 pip install 該缺的模組, 例如至少需要: *** 如果系統說缺啥, 就用 pip install 該缺的模組, 例如至少需要: *** 如果系統說缺啥, 就用 pip install 該缺的模組, 例如至少需要: pip install requests pip install rabboni *工欲善其事必先利其器 ! --- 阿就是說要先花一些時間把工具與環境弄熟, 以後每次都省一些時間, 累計起來就省很多時間! 如果實在不熟悉, 建議看看我以下兩個影片: --- https://youtu.be/HUWjCl0nlHk?t=3 --- https://youtu.be/eDHcMb3jpPA?t=2 (3)到底如何測試 RabboniTsaiwn.zip 的程式碼? (a)解壓縮到一個子目錄, 應該是 Rabboni 裡面有三個子目錄: (第三個不要管它) IoTtalk_v1_BLE --- 藍芽版本 IoTtalk_v1_USB --- USB 版本 (b)建議先用 USB 版本, 這樣只要用 USB 線連接 Rabboni 即可, 進入 USB 版本的子目錄, 修改 DAI.py 要改的只以下兩列: (Line 7, Line 10) Rabboni_BLE_address = 'C9:B4:01:AB:F3:C8' # 要改成你的 Rabboni 的 MAC ServerURL = 'https://class.iottalk.tw' #with SSL connection # 注意你的 project 所用 Server * 注意 雖用 USB 版時 MAC_addr 可亂打, 但仍建議照你 Rabboni 上貼的小紙條打 * 也要注意你用的 IoTtalk Server 名稱, 要跟你 project 所在的伺服器相同! * 要注意別把 https:// 去掉了喔! 還有, 如果用 http:// 那最後要寫 :9999 (c)在 CMD 窗執行 DAI.py (這會假裝自己是 Smartphone 去跟 IoTtalk 註冊) python DAI.py (d)在 IoTtalk 的 project 把 Rabboni 當作 Smartphone 即可, 但是只能勾選 (d1)Acceleration 重力加速度感測器 和 (d2)Gyroscope 陀螺儀 因為我們其實是 Rabboni, 假裝成 Smartphone 而已 :-) (e)改用 BLE 藍芽版本測試的注意事項: (要進到藍芽版本的目錄內) -- MAC addr 一定要正確, 否則連不到你的 Rabboni (USB 版不是很重要:-) -- 執行程式 DAI.py 之前, 要先確定: --- 已經把 Rabboni 的開關鍵(左邊)打開 (用 USB 版可以不必開) --- 按 Rabboni 右邊按鍵讓它閃綠燈 (約五秒閃一下表示在廣播) --- 原則上這時 Rabboni 可以不接 USB 線, 但如果你的 Rabboni 沒足夠的電, 要連接 USB, 可以連到行動電源即可, 不一定要接電腦! --- 當然你的電腦要有藍芽接收器; 通常筆電有內建, 如用 PC 就要接個藍芽接收器 Dongle; (4)關於 Rabboni 六軸感測器的資料, 請參看前述的 Rabboni Rabboni Rabboni 內的投影片(在 ppnt 子目錄內) (5)參看以下這些影片: (在 youtube 內搜尋 iottalk 可找到很多) * EasyConnect DEMO video https://youtu.be/lceuZTaxhN4IoTtalk Cyber Devices Demo https://youtu.be/mmKiqMh_0ec *這裡面有三個趣味影片: *** https://www.youtube.com/channel/UCPkQwrZoVnuilOsw9up9n7g *還有, 這裡也有其他學校用 IoTtalk 結合物理課程: *** 台中教育大學 用 http://120.108.221.66:9999/ 測試 IoTtalk (點入後, 還有更多連結) (6)看看別人更多創意(Idea, 點子): 12u10 創意伸展台 https://12u10.nctu.edu.tw/portfolio/
  *你也可以喔.. 阿就是把你的作品寄給交大幫忙上架 12u10 創意伸展台 
       結合 IoTtalk (沒用到 IoTtalk 也 OK :-), 完成項目:
        1.說明簡報 ppt 
        2.程式碼 sbx (ScratchX) / Python or Others (如果有) 或 project 相關資料  
        3.成品影片mp4  (影片可上傳 youtube 設定公開然後複製網址, 寫在 E-mail 內) 
       請註明作者(學校學號姓名), 將作品E-Mail至: 
       交通大學 陳宛君小姐
       Email: effie@nctu.edu.tw
       連絡電話: 03-5731627   陳宛君小姐
     * 作品將上架至交大12U10網站:
          https://12u10.nctu.edu.tw/portfolio/ 
(7)賈伯斯說過: 創意, 偷就有了!( 會開新窗連到 iottalk.vip/000/#Idea00 ) (其實是..其實是畢卡索說的) ※=> 參看 賈伯斯在1995遺失的訪談中(注意 1:07:05 處)說借用(偷取)別人的點子並不可恥
*** Python + Rabboni 進階程式設計與應用
    參考投影片在 Rabboni 資料網頁的 ppnt 子目錄內
** 在 IoTtalk 平台用 GUI 創建新產品(新的 Device Model / DM; 新設備類別)
      (要在首頁點 Device Feature Management, 然後點左上角可切換 DF 模式 / DM 模式)
   ==> 目的是要更了解如何用 Python 撰寫 DA (Device Application):
       (包括新建一個 Rabboni 的 Device Model; 
          範例 10-7 和 範例 10-8 都是使用 Rabboni 這 Device Model) 
    (1)先從最基本的 Dummy Device 開始
         (四)練習 Dummy Device -- iottalk.vip/000/#Dumm
             這簡單範例會不斷的做 PULL 拉下資料印出, 用亂數生出資料 Push 去 IoTtalk
    (2)接著練習(五)加入使用 Thread 幫忙從鍵盤讀取 "Sensor" (感測器)資料送去 IoTtalk
    (3)參考(六)做更多練習 ! 
       在自己電腦上跑多份的 Dummy Device, 注意若 reg_addr 忘了改會被當作同一個 Device 
       這練習可以幫忙你搞清楚 Reg_addr, dm_name, d_name 這些變數的目的 !

**以上練習 可以參看這教學影片: (歹勢 .. 聲音有點小 :-) 
    0324 P5 -- IoTtalk平台介紹與使用(二) - Part 5/5 Dummy Device練習 
**以下是交大同學的習題: 
          (這習題要用到抓 Bulb 到自己電腦改, 這在另外 0324 Part 2 的影片)
          (Bulb 不是用 Python 寫的; 是用網頁技術寫的, 包括 HTML, CSS, Javascript)
★★ 點左邊的往右三角型可展開, 再點就收起來 ! ** 多做練習題並思考, 就會進步:-) **
**這個練習題其實不難, 照說明一步一步做即可做出來 :-) ==> 即使不繳交也要看0324 P2影片 和 0324 P5影片, 可以練習一下更好!  **這題說簡單雖不是很簡單, 但說難其實也不難(因都已經解釋得很清楚了:-) 題目: 四個Dummy Device + 三個燈泡的聯合測試. 以便更了解 d_name 和 Reg_addr;
   抓 Bulb 和 Dummy Device 原始碼到自己電腦上修改並執行,       測試兩個 Dummy Device 共同控制一個燈泡, 且各自順便控制自己燈泡。
   因為要順便用另兩個Dummy Device印出值, 所以共需四個Dummy Device 和三個燈泡。 Due: 這加分題最晚要在04/06(一)23:59:59之前繳交才可以獲得加分。

規定與說明:   (1)合作控制連接點的joint function第一次一律故意送出 100, 這需用到 global(全域)變數,
   就是函數Save之前要勾選Include non-DF-arguments,    且勾選後彈出的小窗內給該 global 變數初始值( global 要宣告在 function 內)
   除了第一次之外, 一律送出兩輸入之和, 但是加總之前要限制各自輸入上下限為[0, 50],   就是說, 若大於50算50, 若小於 0 阿就算 0 啦(注意 if 條件後要冒號喔)!
      這是要讓兩個Dummy Device的影響力相同, 避免有一個故意打很大或負數影響太大;
    (思考一下: 搭配前述規定第一次故意送出100這樣會有啥效果?)
      但各自控制的燈泡連線則不管它, 就是不要套用function (別忘了系統會 Auto Scale);
      且各自控制值各自順便送一份去不同Dummy Device印出來看(會印出的兩個放螢幕右邊)。
  (2)四份Dummy Device名稱(d_name)寫死,    最左邊分別是字1D-, 2D-, 3D-, 6D-, 名稱最右邊依序是 .IN, .IN, .OUT, .OUT
  各名稱中間是你姓名英文縮寫字母, 例如蔡文能就用TWN三字母;
(中文姓名兩字就用兩字母, 三字用三個字母,四字或以上用四個字母; 第一字母代表姓)    這時我(蔡文能)的四個Dummy Device 名稱是:
    1D-TWN.IN, 2D-TWN.IN, 3D-TWN.OUT, 6D-TWN.OUT;
  (Hint: 這樣需要有四份 DAI.py, 可 copy 手冊網頁內(五)的 DAI2.py 來修改;
                  提醒這 DAI2.py 仍需用到(四)裡面說的 DAN.py 和 csmapi.py)
      再次提醒, 每份的 Reg_addr 都須不同, 否則會被當作同一個Device;    參考0324 P5影片:
            0324 P5影片 
   的解說, 建議切換到 youtube 窗看且設定 1080P;
   *在 youtube 內影片下方我有放詳細 sub topic 的時間點連結可以點跳至該時間點!
  (在新E3內可先點全螢幕, 再點右下角Youtube; 或直接在 youtube 搜尋 iottalk+0324)   (3)四份Dummy Device 連接方式規定如下:
   1D- 和 2D- 的 Dummy Device 合作共同控制一個從Server開啟的Bulb燈泡,
   1D- 和 2D- 的 Dummy Device 也要各自控制一個自己電腦上的Bulb燈泡;
   
1D- 的IDF順便連給 3D- 的ODF監看, 2D- 的 IDF順便連給 6D-的ODF監看;    故意這樣規定!請用Google搜尋"網路線 1-3對調"隨便看幾篇:-)
  (4)三份Bulb, 其中兩份是自己電腦上改過的網頁版:
   合作控制的Bulb燈泡使用 IoTtalk 伺服器上的 Bulb 開啟網頁;    各自控制的兩燈泡d_name名稱最左邊是代表你姓名的三個字母(或兩個字母或四個字母),
      且格式要類似 TWN-38.Bulb, 但這裡的 38 是亂數決定(原版本就已經有用到亂數);
   就是再次開啟自己電腦上 index.html 就可能變 TWN-49.Bulb;    所以, 雖然需要在自己電腦執行兩次 index.html 跑兩個燈泡, 程式碼只要一份即可!
   請注意這在 iottalk.vup/000/ 沒有參考資料喔 :-)
   可參考0324 P2 影片:
             0324 P2 影片
    的解說(已熟悉CMD環境者可從 32:18 處開始看即可:-)    *在 youtube 內影片下方我有放詳細 sub topic 的時間點連結可以點跳至該時間點
  (5)另外, 為了CMD窗畫面乾淨些, 請修改 DAN.py
   把印 Device state 的兩句都用 # 註解掉 (大約在 Line 32 和 Line 34)
   其它原則上不必修改, 但建議稍為研究一下各程式碼 :-)   (6)三個燈泡都放右邊, 兩個跑 .IN (Sensor 端)Dummy Device的CMD窗放左邊,
   兩個執行.OUT(Control端)Dummy Device之CMD視窗放右邊;    還有,
   整個 project 除了前述共同控制連接點套用一個 function 之外, 不要套用任何函數。  (7)提醒: 兩輸入的 joint 函數在只收到一個輸入值時還不會做輸出是正常的!
繳交(Turn In):   
  (a)類似第一題規定, 要有練習之心得至少35字至多300字,              以及至少五張螢幕截圖, 至多七張, 包括:
    (a1)看到project連接圖且看到控制燈泡的 joint 有套用函數,     (a2)看到所套用函數之內容,
    (a3)看到測試時左邊兩Dummy Device輸入不同值時燈泡亮度之畫面(至少三個不同狀態)       例如, 兩個都輸入0, 兩個都輸入大於 50 的不同值, 再來兩個都 25, ..其它..
  (b)左邊兩個跑 .IN Dummy Device之CMD窗內輸入之次數可以不同,     但請不要在輸入超過15次後截取畫面(如果d_name還沒捲不見就可以),
    就是說, 四個Dummy Device的CMD畫面上都必須仍可看到 Device name;    如已經看不到Device name, 請輸入 quit 後重新執行並重新做關聯綁定再測試!
  (c)注意測試畫面儘量可看到全部Device和監看(monitor)聯合控制燈泡之 Joint 進出值。
Hint(提示):
 (1)先把一個左邊的 Dummy Device 的 IDF 接到共用燈泡(注意規定這燈泡從 Server網頁開啟)
   再把左邊另一個 Dummy Device 的 IDF 連接到其 Joint 連接點, 以便共同控制該燈泡。  (2)Joint function 要把Include non-DF-arguments 打勾,
     在勾選時彈出小窗輸入 ggyy = 1
     並在函數內寫 global ggyy   (當然可以用其它變數名稱)
     這樣, 函數內可檢查若 ggyy 是 1 就是第一次,      此時可做第一次要做的事且要把 ggyy 改為 0 (或其它不是 1 都可)。
 **這是要讓大家了解 global 變數用法以及可用來判斷函數是否第一次被執行!  (3)在兩個輸入連結點函數內兩個Dummy Device之輸入可用 args[0] 和 args[1] 取得。
 (4)關於 Bulb, 因為原版的 Reg_addr 就是用亂數,    所以 Bulb 不必處理 Reg_addr, 每份都是不同 Bulb (燈泡)
 (5)關於 Dummy Device, 要複製(五) DAI2.py 為四份各自改 d_name 和 Reg_addr,    但再次提醒, 它仍須用到(四)說的從 github 抓來的Dummy Device其它程式, 所以請放同一個目錄。
   還有, 如果 Reg_addr 相同, 即使 d_name 不同仍會被 IoTtalk 伺服器看作同一個 Device;    反之, 如 Reg_addr 不同, 但 d_name 相同(例如都是"李登輝"), 系統仍看作不同 Device;
   問題是,    此時當你在 project 的GUI想做關聯綁定時,    右邊畫面出現兩個"李登輝" 阿你就不知道哪個是哪個囉!  (6)注意網頁上 (五) DAI2.py 已經把 IoTtalk Server 寫 demo.iottalk.tw   還有, 也注意設 d_name 的 Line 19 被我用 # 註解掉,   記得去掉 # 並各自給規定的 d_name 名稱。  (7)另外, 為了CMD窗畫面乾淨些, 請修改 DAN.py   印 Device state 的兩句都用 # 註解掉 (大約在 Line 32 和 Line 34)   其它原則上不必修改, 但建議稍為研究一下:-) **明天(03/27星期五)會在 iottalk.vip/000/ 的 (六) 區放一張我測試的project連接圖 (其實前面已解釋) **後天(03/28星期六)會在 iottalk.vip/000/ 的 (六) 區再放一張套用函數之內容的圖(故意弄成圖)
================================

(4)開始研究 Rabboni 的一些程式碼: 點這開新窗跳到 iottalk.vip/000/ 的(十)Rabboni 小玩具 ..     **請依照 10-1, 10-2, ... 10-6 順序練習    **上述六個練習並沒用到 IoTtalk 平台 ** 參看Rabboni簡介影片: Rabboni 實體簡介與使用須知 *** 參看Rabboni + IoTtalk 教學影片: https://youtu.be/-PXQDZX-EHk?t=706 (善用 youtube 內影片下方的說明與子題目連結)
(5)要結合 IoTtalk 的 API 和讀取 Rabboni 資料的 程式碼: (這次 Rabboni 不再假裝自己是 Smartphone 了:-) (這次 Rabboni 不再假裝自己是 Smartphone 了:-) (這次 Rabboni 不再假裝自己是 Smartphone 了:-) ** 10-7(USB版) 和 10-8(BLE版) 要結合 IoTtalk Server, 所以需要之前的 DAN.py 和 csmapi.py --- 原先的 DAI.py 改用 10-7 程式碼(USB版) 或 10-8 的程式碼(BLE版) --- 要用 GUI 新建立一個 DM (Device Model),       例如叫做 Rabboni ; 範例10-7, 10-8 是要搭配這樣叫的 DM (Device Model): DAN.profile['dm_name']='Rabboni' # 要在 IoTtalk 平台建立對應的 Device Model --- 範例中我們故意弄四個 DF (Device Feature; 功能): DAN.profile['df_list']=['Acceleration', 'Gyroscope', "Float", "Total"] ***** 因為 0.8版之後沒有 rabbo.Cnt 可用, 所以程式碼鐘關於 .Cnt 已經改掉或注解掉 ! ***** 你可以發揮創意改弄出其他值送去伺服器給 'Float' 和 'Total' (注意這在 DM 鐘故意設是字串) ==> 注意 要用到 之前說的 Rabboni Rabboni Rabboni 的程式碼 DAN.py 和 csmapi.py DAN.push('Float', myFloat) DAN.push('Total', str(tot) ) # 因為 Total 是字串, 建立 Rabboni DM 時故意的
*** 參看Rabboni + IoTtalk 教學影片: https://youtu.be/-PXQDZX-EHk?t=2938 (善用 youtube 內影片下方的說明與子題目連結) *** 補充 .. 在上述 10-7 (和 10-8) 須要建立新的 DM, 這會用到 Device Feature Management 和 Device Model Management, 可參看以下兩影片: (a)Device Feature 管理: 0505 Part1 DFM Device Feature Management (b)Device Model 管理: 0505 Part2 DMM Device Model Management
** 10-8 是 BLE 藍芽版, 再次提醒: 執行程式之前, 注意你的 Rabboni .. -- MAC addr in DAI_Rab5b.py 一定要正確, 否則連不到你的 Rabboni -- 執行程式 DAI_Rab5b.py 之前, 要先確定: oo 已經把 Rabboni 的開關鍵(左邊)打開 (用 USB 版可以不必開) oo 按 Rabboni 右邊按鍵讓它閃綠燈 (約五秒閃一下表示在廣播) oo 原則上這時 Rabboni 可以不接 USB 線, 但如果你的 Rabboni 沒足夠的電, 要連接 USB, 可以連到行動電源即可, 不一定要接電腦! oo 當然你的電腦要有藍芽接收器; 通常筆電有內建, 如用 PC 就要接個藍芽接收器 Dongle:
 
簡介 (Introduction)
IoTtalk 系統交通大學林一平博士帶領研發團隊開發的智慧物聯網應用開發平台
目前這 IoTTalk 已經成功用於智慧校園以及智慧農業, 例如用於種植薑黃(5分鐘影片)!
更詳細的介紹請看:
    https://iottalk.vip/000/   (即 IoTTalk 物聯網應用操作入門手冊)

     使用 IoTtalk 系統最大的好處是你可以利用 GUI 快速建立應用專案(在首頁點 Project 進入 GUI 畫面), 針對任一個應用專案, 可以加入一個或多個輸入和輸出的設備模型(Device Model), 用滑鼠點按即可連接輸入設備和輸出設備(單點功能連接或多點功能連接),當物聯網設備連接至 IoTtalk 系統時,針對各種感測器,IoTtalk 會自動產生或使用應用軟體來處理,因此每一個輸入設備可以相當方便地連接至輸出設備,必要時還可用 Python 在連結點寫簡單前處理或後處理函數;如果是多點連接, 甚至可在連接點撰寫較複雜的處理函數。

      建立 IoTtalk 應用專案之後, 可以隨時將專案中的設備模型綁定(Bind)或稱關聯(Associate)到任一個該模型的真實IoT設備, 或定/關聯到事先寫好的虛擬設備(Cyber Device 或 VPython 設備)或自己用程式寫的虛擬IoT設備, 這樣就能快速實現你的創意體驗物聯網應用。

     針對任一應用專案(Project), 在 GUI 操作介面也可以隨時增減設備模型(DM),修改要使用的設備功能屬性(DF), 隨時切換連結方式與對象,更可以隨時改變各設備模型關聯(綁定)到的真實或虛擬設備。

      還有, 從首頁點 Device Feature Management 可進入維護管理 Device Feature 和 Device Model 的管理畫面(進入管理畫面後可以點左上角切換DF管理/DM管理), 不但可以修改維護已有設備模型(Device Model)的設備功能屬性(Device Feature), 也可以建立新的設備功能屬性(Add new DF), 甚至也可以建立新的設備模型(Add new DM), 使得建立IoT物聯網應用更有彈性。

* 關於如何使用 IoTtalk 平台體驗物聯網應用,
    請參看 IoTTalk 物聯網應用操作入門手冊

Top  幾個英文字讀音
   
Terminology 相關術語:
Server 端 (Network Domain)
EC(Execution and Communication system,縮寫EC;執行與通訊系統) = CSM + ESM
    CSM 和 ESM 合起來稱 執行與通訊系統 (Execution and Communication system,EC)
CSM Communication SubModule system lib/csm.py
   通訊子模組系統(CSM) 定義 HTTP based RESTful API (Application Programming Interface),
   提供給設備應用(Device Application,縮寫 DA;) 用來傳送或取得輸入/輸出設備資訊。
   當一個物聯網設備註冊/取消註冊 (registers/deregister) 到 EC 時,
   DA 會經由 HTTP API 要求 CSM 去改變資料庫中之設備狀態,
   當物聯網設備完成註冊至 DA 後,即可以經由 EC (CSM+ESM) 彼此相互通訊。
ESM Execution SubModule system lib/esm/esm_main.py (import by lib/esm/__main__.py)
    執行子模組系統 (Execution SubModule system,ESM) 負責執行網路應用,以連接相關輸入與輸出功能。
SIM SIMulation Input Device Feature (IDF) lib/ecsim/ecsim.py
    模擬輸入功能模組,啟動這功能可模擬輸入設備的"輸入", 以驗證輸出設備功能是否正常。
CCM Creation, Configuration and Management system lib/ccm/ccm.py
    建立、設定與管理系統 (Creation, Configuration and Management system,CCM)
    負責建立與管理物聯網設備,自動設定輸入與輸出功能之連結,儲存所有相關資訊於資料庫系統 (DB)。
GUI (Graphical User Interface)是 CCM模組( lib/ccm/ccm.py套用 lib/ccm/templates/index.html產生出 (render) 來的網頁(點首頁Project)
  主要用到 lib/ccm/static/gui.js 這 Javascript 腳本


WEB the Web portal (port 80) da/web.py
    ** 注意如果啟動 Nginx 轉送 CSM, 則不要執行 da/web.py
 
    註: IDA 通常是 Sensor and/or Actuator (感測器 and/or 控制引動器/執行器)
              所以, 現在我們已經把 IDA 改名為 SA 或 SAA; 它也可能是有燒錄韌體的嵌入式設備!
Client 端 (Device Domain)
  DA  -- Device Application(設備應用程式;縮寫DA),
     通常是在行動裝置如手機內(開啟網頁或APP),
     DA用 HTTP based RESTful API 與 CSM 溝通(IoTtalk系統提供 csmapi),
     關於 CSM 的處理程序, 可以在 lib/ 目錄內用以下命令大略看:
        grep app.route csm.py
        grep app.route csm.py -3n    # 會列出該列的前後 3 lines 且有列編號
  DAI  -- Device Application to Iot device
  DAN  -- Device Application to the Network(to IoTtalk Server)
  IDA  -- Iot Device Application, 物聯網裝置的感測器或控制器(現在我們已經改名叫 SA),
         通常用 NodeMCU/ESP8266/ESP32等連接物聯網裝置,
         IDA (SA/SAA) 以無線網路和 DA 的 DAI 溝通, DAI轉由 DAN 與 CSM 溝通。
    ** 要連接實體裝置,建議看 ArduTalk-for-NodeMCU ( 這是使用 NodeMCU-ESP8266 )
      另外, 我們也有 ArduTalk-for-ArduinoYún (使用 Arduino Yún 開發板) (重要工作都用這個:-)

    ** Web-based 的 Client端用到 jQuery Library, 不熟的建議看看 jQuery Tutorial.
          Bulb (燈泡) 就是一個 Web-based 的範例, 有興趣研究 Bulb 範例的 可以參看這教學影片:
          0324 Part2 -- 抓 Bulb 範例到自己電腦修改並執行
Top  幾個英文字讀音
GUI提供使用者建立/修改 IoT應用專案的 GUI操作畫面, 如下圖所示:  
* 使用者經由 GUI 操作指示 CCM 做事,
    包括建立/修改 Device Model/Device Feature, DF function,
    以及物聯網應用Project的增刪設備設定與關聯等各項操作;
    ** 這裡說的"指示 CCM 做事,"
      (a)在瀏覽器 HTTP Client 端主要是
          lib/ccm/static/gui.js 調用 lib/ccm/static/ajax.js 內各函數發出HTTP請求給CCM;
     
(b)在 Server 端的 CCM 就是 lib/ccm/ccm.py
          你可以在 lib/ccm/ 目錄內用以下命令大略看看有哪些處理程序:
              grep app.route ccm.py

      或用額外關鍵字過濾, 例如 :
              grep app.route ccm.py | grep device

Top

*在 lib/ccm/static/gui.js 中總共提供了30個事件處理函數(event_handler function),
    分別處理使用者操作觸發的各種事件, 可在 gui.js內搜尋 functionevent_handler
    這些事件可以分為五類:
        Device Model 處理可用的設備 、 Device feature 處理設備功能相關 、
        Device Object 處理Project專案用的設備 、 Function 處理專案內的 Python 小函數 、
        Connection 處理專案內連接輸入設備與輸出設備

*操作GUI網頁過程中, 網頁的變化是透過調用 lib/ccm/static/make.js 裡面的各 make* 函數
;
*此外, 操作過程中會 需要與 IoTtalk Server 端互動, 包括取得/更新資料庫內容,
    這會調用 lib/ccm/static/ajax.js 內各 ajax_ 開頭的函數;
    主要是用 HTTP request 丟給 Server 端的 lib/ccm/ccm.py 檔案內對應的函數處理
*還有, 上圖中可看到檢視網頁內有用到 js/main.js 就是 lib/ccm/static/js/main.js
    那個是一個 Custom jQuery function

* 如果你不熟悉 jQuery, 可先看看維基百科 還有 這 jQuery 簡易教學   2   3   4   5   6   10  
      或是 看這 jQuery教學影片(兩小時)
Top  幾個英文字讀音
  關於 uwsgi, Nginx 和 https 相關等...
    IoTtalk Server 的 CSM 和 CCM 都是用 Python Flask 框架(Framework)實作的 Web Server,
雖然開發Flask Web程式時用 Python 直譯器, 寫好了通常就改用支援 WSGI 的執行環境。
(你在用 Python 手動執行 Flask Web時會看到訊息說Flask產品建議用支援 WSGI 的執行環境!)

    在IoTtalk啟動檔 setup/startup.sh 內已經寫好可以選用 uwsgi 取代 Python 開發環境,
如果你使用我們提供的自動安裝腳本如 iotubu, 在執行中會詢問你使否要用 uwsgi+Nginx ?
如果你回答 yes 則在 setup/startup.sh 會改用uwsgi執行CSM和CCM模組(並自動安裝 Nginx)。

    uwsgi 是支援WSGI的 HTTP Server,它是第一個實作WSGI協定的Python 執行環境(Runtime environment),
另外常聽到的還有 Bjoern, CherryPy, Gunicorn, Meinheld, mod_wsgi 等, 這裡有人做了評比
 
WSGI 全稱是 Web Server Gateway Interface,中文翻譯做 Web 伺服器閘道介面,
它是 Python 語言定義出來的 Web 伺服器和 Web 應用程式之間的簡單而通用的介面,
是基於現存的 CGI (Common Gateway Interface)標準設計,通常網頁是被從Web Server抓回
我們電腦的瀏覽器執行,但 CGI 程式是在 Server 端直接執行,CGI可以是任何語言寫的程式,
通常CGI程式會存取資料庫然後吐出新網頁, 如果還不清楚 CGI 和 FORM 的就Google一下吧。

 
TOP
* 如何增加/修改 Device Model 物聯網設備類別 ?                 
    (0)阿這是IoTtalk使用者該知道的喔, 在使用者手冊內有詳細解說 !
        這與如何增加/修改/刪除 Device Features(設備功能屬性)在同一個操作介面,
        都是從IoTtalk首頁點選 Device Feature Management進入頁面;
        這時 左上角是Device Feature, 用滑鼠點一下就可切換 Device FeatureDevice Model 管理頁面!

    (1) 在iottalk 首頁點選 Device Feature Management
          這時可以新增新的 Feature(功能), 不過因裡面已經很多, 通常不需要再新增了 !
          阿就拿已經有的功能來創造你的新設備類別(Device Model)就可以囉 !
    (2) 點左上角的 Device Feature 切換為 Device Model 畫面
          這時可以新增新的 設備類別(Device Model),
          也可以把已經有的 Device Model 調出來研究, 修改後另存成新的 Device Model
     
    (3) 右邊視窗中點選 add new DM
    (4) 然後選 IDF 或  ODF, 選擇類別 Category 然後選你要的功能
    (5) 最後記得要點 Save 存檔, 它會問你 Device Model Name
    (6)注意 Device Model name 若與已經有的重複會警告你要蓋掉 !
    (7)成功加入之後, 就可在 Project 中新增這個Device Model 設備類別了
Top
 
* 如何撰寫連接 IoTtalk 的 Device Application ?
* 建議先看看林芸蔚博士提供的這 Dummy_Device_WebVersion
    然後再 看 da/Bulb 目錄內 index.html 和 da/Bulb/js/ 內各檔案 (github 上的比較舊)
  我把它放在 iottalk.vipAtest (在 Bulb 上方)
  你可以自己剪事在你安裝部署 IoTtalk Server內的 da/Bulb/ 內檔案 )

* 再看看林芸蔚博士提供的這 Dummy_Device (Python version)

* 如果你要用 NodeMCU/ESP8266 連接真正的物聯網設備,
    可看林芸蔚博士提供的   這 ArduTalk for NodeMCU
     (裡面有Ardutalk-NodeMCU-ESP8266安裝與操作教學)

* 還有如果要用 Arduino Yun 板 則要看 這 ArduTalk_for_ArduinoYun   (Python 程式)
   
* 如何增加 VPython Device ?
  * 所有在 da/vp/py/ 子目錄下的 *.py檔案, 都會被當作一個 Vpython Device,
      你可以故意在 da/vp/py/ 目錄下 touch ABC.py 生出一個空 ABC.py 檔(注意大小寫)
      然後瀏覽器 IoTtalk 首頁 按 F5 重刷新你的網頁, 就會發現多了 ABC 的 Vpython Device
      當然這點了也是不能用, 不過你可以這樣 cp Ball-throw2.py BAA.py
      然後瀏覽器 IoTtalk 首頁 按 F5 重刷新你的網頁, 在 Vpython List 下點 BAA 這 Device
      會發現與原先的 Ball-throw2 完全相同 !
    * 所以參考 da/vp/py/ 目錄下的 .py 檔案就可以寫出新的 Vpython Device 囉 !
          點這偷看這 da/vp/py/Free_Fall2.py    (很短, 才 38 列 :-)
          點這偷看這 da/vp/py/Ball-throw2.py
          (建立一個 Project 用 Smartphone 選加速度 IDF 連接這Ball-throw2就可用你的手機玩丟球的遊戲 !)

* 要了解VPython, 可以看 王一哲的VPython 教學文件
    還有, 可以 點這看曾靖夫的建中VPython & VPhysics特色課程
要測試使用IoTtalk 可以連這:   http://demo.iottalk.tw關於一些英文字 543 五四三若想租用 VPS 建議用 Hostwinds.com 或流量小可以先用免費的 AWS EC2

  TOP
      Q   &   A                              


Q1: 我用 vim 編輯檔案時很奇怪且字的彩色又看不清楚怎辦 ?
  A: vim 和 vi 都會讀取 .vimrc 的設定, 如果只是討厭彩色字體不清楚, 只要打入
      :synt off
    取消 vi/vim 根據文字的語法顯示顏色;
    如果是討厭在插入文句時自動縮排甚至自動在左邊補 # 這種雞婆動作, 那就改 ~/.vimrc 設定;
    如果你搞不清楚, 阿就做以下三列命令:
cd
wget iottalk.vip/vimrc   
cp -p vimrc .vimrc

  Q2: 網頁如何和 Python flask 程式之間互傳資料 ?
  A: 我寫了一個簡單的網頁 echotest.html   ( 用到 Javascript 和 JQuery )
    可以把 JSON 資料用 POST 方式傳入 Python 程式 da/web.py 的 /echoans,
    在 /echoans 處理之後, 會套用 echoans.html 把結果回傳給 echotest.html,
    然後在 echotest.html 內用 document.write( 結果 ) 產生新網頁:
     點這測試 iottalk.vip/echotest
    ( 網頁內有連結可以讓你檢視各相關檔案, 看完應該就會了 :-)
    為了方便檢視, 以下是在 da/web.py 內關於 /echotest 和 /echoans 的程式碼:
@app.route('/echotest')
@app.route('/testecho')
def echotest():
    return render_template('echotest.html', name='Tsaiwn')

@app.route('/echoans', methods = ['POST', 'GET'])
def processEcho():
    if request.method == 'GET':           # 如果從網址打 /echoans 就會是 GET
        return redirect('/echotest')      # .. 那就轉址到 /echotest
        #return redirect('http://{}/echotest'.format(request.host))
    # read json "data" that "POST" to me, then pass "data" into  echoans.html, along with OS Information
    data = request.get_json(force=True)
    #print("Got ",data)
    osinfo = subprocess.check_output("cat /etc/*release", shell=True).decode("utf-8")
    nginxinfo =  subprocess.check_output("cat /etc/nginx/sites-enabled/*", shell=True).decode("utf-8")
    return render_template('echoans.html', data=data, name='蔡文能', osinfo=osinfo, nginx=nginxinfo)
      點這看 echotest.html             點這看 echoans.html
Q2b: 可否從 Python flask 程式內直接把資料回傳, 不要套用 HTML 樣版檔案?
  A: 當然可以, 我另外改寫一個 /echotest22 示範用 JQuery 處理回傳資料,
    也順便示範把 JSON 資料塞到一個 Table 排整齊 (cars 的資料)
    請點這測試 http://iottalk.vip/echotest22
    點這看 echotest22.html (會被在 web.py 內的 /echotest22 這 endpoint 拿來 render)
    以下則是在 da/web.py 內關於 /echotest22 和 /echoans22 的程式碼:
@app.route('/echotest22.txt')
def echotest22_txt():
    return send_file('templates/echotest22.html', attachment_filename='echotest22.txt')
@app.route('/echotest22')
def echotest22():
    return render_template('echotest22.html', name='Tsaiwn')

@app.route('/echoans22', methods = ['POST'])
def processEcho22():
    data = request.get_json(force=True)
    result = ''
    for item in data:
        # loop over every row
        make = str(item['make'])
        if(make == 'Porsche'):
            result += make + ' -- 好車 That is a good manufacturer\n<br>'
        elif (make == 'Benz') :
            result += make + ' -- <font color=red>哇哇 That is a car for  有錢人</font>\n<br>'
        else:
            result += make + ' -- 普普 That is only an average manufacturer\n<br>'
    #print("result=\n", result)
    return result
    #return Response(result, mimetype='application/text', status=200)
    #return jsonify(result)
    #return Response(result, mimetype='application/text')
    #return Response(result, mimetype='text/html', status=200)
            點這看 echotest22.html
      ** 因為 /echoans22 在 web.py 內直接回傳結果, 所以沒有 echoans22.html 喔 !

Q2c: 可否像一般FORM/CGI 那樣用 Form 的 action 丟給 Python flask 處理?
  A: 阿這也類似啊, 我改寫成 /testform (這直接由 da/web.py
      的 @app.route('/testform') 那段生出網頁)
    請點這測試 http://iottalk.vip/testform
      (會把 FORM 資料丟給 da/web.py 內的 /ansform 處理)
    也可以 點這看用來套用 FORM 資料的 ansform.html
    ** 注意, FORM 的 action 建議不要用 GET 或 PUT,應該用 POST 喔 ! (Why ?)
    以下則是在 da/web.py 內用來回應 /testform 的 /ansform 的程式碼:
########################################
@app.route('/echoform', methods = ['POST', 'GET'])
@app.route('/ansform', methods = ['POST', 'GET'])
def echoformtest( ):
   if request.method == 'GET':
      return redirect('/testform')
   data = request.form
   osinfo = "Unknown, maybe is Windows"
   nginxinfo = "empty nginx"
   try:
      osinfo = subprocess.check_output("cat /etc/*release", shell=True).decode("utf-8")
      nginxinfo =  subprocess.check_output("cat /etc/nginx/sites-enabled/*", shell=True).decode("utf-8")
   except:
     print("echoform error")
   return render_template("ansform.html",result = data, name='蔡文能 tsaiwn',nginx=nginxinfo, osinfo=osinfo)
    也可以 點這看用來套用 FORM 資料的 ansform.html
    ** 注意並沒有 testform.html, 因為 /testform 是在 da/web.py 內處理 /testform 直接生出網頁 !
          請點這測試 http://iottalk.vip/testform   進入網頁後, 可用滑鼠右鍵點選檢視網頁原始碼

Q2d: 可以用 GET 方法傳嗎 ?
  A: 當然可以, 不過網頁 FORM 的 action 建議不要用 GET 或 PUT,
      因為那樣會使得 URL 和參數都出現在網址列 !
      所以傳密碼千萬不要用 GET/PUT 喔!
    我另外寫一個 /testj 可以把網址列 /testj/字串一/字串二 GET 傳入產生的網頁,
    然後你可以選擇點一個連結 "GET" 到 Server, 由 Server 回傳成 JSON 資料 蓋掉網頁!

    或, 點我做的按鈕, 會用 jQuery $.get() 把資料傳到 Server, 由 Server 回傳成 JSON 資料,
      再用 JQuery 把 JSON 資料塞入一個事先準備的 div 區域中 !

    點這測試 "http://iottalk.vip/testj/ha haha/He he Heee


Q:
  A:


Q:
  A:


 
    To  be continued    

           



 
****** 在自己電腦上跑一個 Python + Flask 網站 (這可搭配 VPython DA)
(1)參考以下建立 python 檔案例如 w80.py
(2)建立一個子目錄 static 和一個子目錄 templates  (注意最後有 s)
       ==> 原則上 .html 檔案放 templates 內, 其它檔案放 static 內  (當然 .html 也可放 static 內, 用法不同而已)
    ** 執行 w80.py 後, 假設你 port 用 5566  (就是以下 myPort = 5566 ) 
       在 static 下的 檔案 可以用  http://127.0.0.1:5566/static/檔案名稱  抓到
       ==> 不過, 自己網站內網頁互相調用只要寫  /static/檔案名稱  即可
       ==> 如果用 port 80, 則網址中的 :80 可省去不用打
    ** 在 templates 下的 html 檔案要用 return render_template('html檔案')  
(3)執行 python w80.py   
   若出現說少了啥就 pip install 啥 
   ** 這 w80.py 是聽 port 80; 若要改聽其它 port 例如 5566 就改檔案很前面的 myPort=5566
   ** 注意跑起來用瀏覽器打 127.0.0.1 不要呆呆打 0.0.0.0 喔
   ** 若要給外面網路用, 要知道電腦 public IP; 若沒 public IP 則可搭配 ngrOK 使用!
      (註: ngrok 只要去抓檔案回來執行一下打幾個字即可, 大約一兩分鐘可搞定) 
           ==>   可以點這參考我以前給外文系大一計概的習題說明
#這是 Python Flask 網站 w80.py 範例
#This 檔案 uses UTF-8  Filename: w80.py  # 只是為了方便記得這檔案是否用 port 80
#  coding=utf-8   #這是第二列; 注意 coding 句子只在前兩列才有效 !
myPort = 80
## 請在這檔案的目錄建立兩個子目錄: static 和 templates   (注意最後有 s)
from flask import Flask, request
from flask import render_template, redirect, send_file

app = Flask(__name__)
from flask_cors import CORS, cross_origin   #其實這些跨域支援的部分都可以不要
CORS(app, resources=r"/*")
@app.after_request
def after_request(response):
    response.headers.add('Access-Control-Allow-Headers', 'Content-Type, Authorization')
    response.headers.add('Access-Control-Allow-Methods', 'GET, POST, PATCH, DELETE, OPTIONS')
    return response

@app.route("/")
@cross_origin("*")
def webRoot( ):
   return '<font color=Red size=7>Hello World!  &nbsp; &nbsp; 這是網站喔 !</font>'
   '''try:
      return render_template('index.html') 
   except:
      try:
         return app.send_static_file('index.html')  # this file is /static/index.html
      except:
         return "<h1 style='color:red;'>No index.html in templates and static</h1>", 404 '''

@app.route('/abc/')
@app.route('/abc')     # 這列可不寫, 但寫了可以避免最後沒打 "/" 會多轉址一次 
def justAuniqueFunction():
  try:
    return render_template('haha.html')   # 記得要放 templates 子目錄內 (注意最後有 s)!!!
  except: return "<h1 style='color:Red' >找不到 haha<font color=black>haha.html</font> 喔!</h1>",404

@app.route('/b1/')
@app.route('/Ball-throw1/')   # 利用 redirect 轉址讓使用者少打一些字 :-)
@app.route('/b1')
def showTheBallpy():
    return redirect('http://{}/static/Ball-throw1.py'.format(request.host))   

@app.route('/gg')   # 這樣只有 http..../gg 有效; 若寫 http..../gg/ 則沒效!
@app.route('/yy/')  # 這樣 http.../yy/ 和 http.../yy 都有效; 但 http.../yy 會自動轉址(多一次http請求)
def yyAndGg():
  try:
    return send_file('static/b1.py', \
      attachment_filename='Ball-throw1.py')  # 注意注意注意:上列要 static 不是 /static 
  except Exception as e:
    return str(e)

import os
from flask import send_from_directory
@app.route("/favicon.ico")
def helloGiveIC( ):  # FAVorite ICON 是微軟發明的 ; 建議 32x32 .jpg or .png 
   # return send_file('static/myicon.png')     # 注意是 static, 不是 /static  (沒有 / )
   try:    # 先試 /static/myicon.png 看看有沒有?
       return send_from_directory(os.path.join(app.root_path, 'static'),
             'myicon.png', mimetype='image/vnd.microsoft.icon')
   except:  # 試 web 根目錄的 myic.png  (故意用不同檔案名稱)
       return send_from_directory(app.root_path,
             'myic.png', mimetype='image/vnd.microsoft.icon')

if __name__ == "__main__":    # 注意瀏覽器的網址列要用 127.0.0.1:port 不是 0.0.0.0
   app.run( '0.0.0.0' , port=myPort, debug=True)    # 如 port 是 80 則可不用打 :80; debug 使其自動重跑

*** 詳細可以 點這看影片說明(善用影片下方說明與子題連結)
=== 簡單說明 Python 的 pip 與套件(程式庫)管理以及 requirements.txt 

**建議 Python 版本用 3.7.7  (不要用 3.8.x 也不要低於 3.7.5)
**最好建立隔離環境 (雖然可以不必), 以下會建立子目錄 ggyy 
      python -m venv ggyy

**想要啟動隔離環境 ggyy  (解除環境只要打 deactivate 即可)
      ggyy\Scripts\activate

***關於 requirements.txt  用途 (其實名稱可自己定, 我都偷懶打 rr.txt)
***認為Python環境 OK 時, 把用到的程式庫及版本記錄到一個文字檔案
    pip freeze > requirements.txt

***以後新環境或新隔離環境想安裝同樣版本程式庫:
    pip install -r requirements.txt

***如發現不認識 pip 就改用 Python 內建 pip
  python -m pip freeze > requirements.txt

***如不認識 pip, 最好先叫它 安裝 pip 以後方便使用
  python -m pip install --upgrade --force-reinstall pip

#############################

 
台鐵火車查詢-- 高鐵首頁/查詢
You are the website counter -th visitors.             The W3C Markup Validation Service
*
點這看如何快速安裝 IoTtalk 系統 (要先花錢取得交大授權才可以喔:-)
* 點這看如何選擇 VPS ?(建議用 Hostwinds.com 或流量小可以先用免費的 AWS EC2)

* PuTTY official site to Download PuTTY   PieTTY project official site   Download Pietty0400b14.zip
 
  整理 by 蔡文能 tsaiwn@cs.nctu.edu.tw     交大資工系     tsaiwn.weebly.com     LINE ID: tsaiwn     FB: fb.me/tsaiwn     幾個英文字讀音   TOP
  TOP   Find your FB ID: https://findmyfbid.com/ if you want to Find your Facebook ID for your fb:admins