//(app.ino)
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include "app.h"
const char ssid[] = "your-picoW-SSID"; // SSID //使用するpico W(サーバー)用のSSID
const char pass[] = "your-picoW-password"; // password //使用するpico W(サーバー)用のパスワード
const IPAddress ip(192, 168, 5, 18); //使用するclient(your Androidなど)にpico Wが割り当てる local IPアドレスを指定
//(192, 168, 5, 18)は、例
const IPAddress subnet(255, 255, 0, 0); // サブネットマスク
WebServer server(80); //ポート番号80を指定
int PlayMode = 1; //1:演奏 2:一時停止
int GpPwm = 28; //PWM出力 GPIO番号 //ピン番号:34
int GpLedRed = 0; //LED赤 GPIO番号 //ピン番号:1
int GpLedGreen = 1; //LED緑 GPIO番号 //ピン番号:2
int GpPlayPauseSw = 2; //Play/Pause SW //ピン番号:4
int ix; //音符上の休止無音時間チェック
uint64_t interval_T; //[μsec] //LED ON時間、LEDのOFF時間
//interval_T=5msec/実測=6msec, 2msec/3.2msec, 1mse/2msec, 500μsec/1.7msec, 200μsec/1.5msec, 100μ/1.25msec
uint64_t current_T; //現在時刻 //pico起動後の経過時間[μsec]
uint64_t target_T; //LED状態遷移時刻
int SwCount; //スイッチ接触検出数
typedef enum //音符解読ステート
{
SET_SOUND_FREQUENCY_TIME,
CHECK_SOUNDING_TIME,
STOP_SOUNDING,
CHECH_SHORT_SILENT_TIME,
STOP_SHORT_SILENT_TIME,
CHECK_SILENT_TIME
} STATES;
STATES states;
//Android画面表示
char html[] = R"(
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>PWM Melody Producer by Pico W, with Android-type WiFi remote controller </title>
<style>
.top_block {
margin-top:100px;
margin-left:50px;
}
.buttonA {
display: block;
width: 150px;
background: #c8eac3;
color: #000;
padding: 10px;
box-sizing: border-box;
text-align: center;
text-decoration: none;
border-radius: 30px;
}
.buttonA:active {
background: #1a720d;
color: #FFF;
}
</style>
</head>
<body>
<div class = "top_block">
<h3>picoWによる<br>
 スマホ式WiFiリモコン付<br>
  PWMメロディー発生器<br></h3>
<br>
<button type="button" class="buttonA" onclick="location.href='/myTouch'"><b>Play/Pause</b></button>
</div>
</body>
</html>
)";
void OnkaiOut(int doremi, float duty) //PWM出力
{
analogWriteFreq(doremi); //周波数設定
analogWriteRange(1024); //分解能 10bit:1024
analogWrite(GpPwm,1024*duty); //PWM出力ピンに出力
}
void ClosePWM(void) //PWM出力停止
{
analogWriteFreq(100); //100Hz
analogWriteRange(1024); //分解能 10bit:1024
analogWrite(GpPwm,0); //Duty = 0;
}
void SwCheck(){ //SW操作検出チェック
if(digitalRead(GpPlayPauseSw) == 0)SwCount++;
else SwCount = 0;
if(SwCount == 20){ //チャタリング対策
if(PlayMode == 0)PlayMode = 1;
else PlayMode = 0;
}
if(SwCount >= 21)SwCount = 21;
}
void LedCont(){ //LED制御
if(PlayMode == 1){ //再生中
digitalWrite(GpLedRed, 1); //LED赤: 点灯
digitalWrite(GpLedGreen, 0); //LED緑: 消灯
}
else{ //一時停止中
digitalWrite(GpLedRed, 0); //LED赤: 消灯
digitalWrite(GpLedGreen, 1); //LED緑: 点灯
ClosePWM(); //PWM出力停止
}
}
// サーバーリクエスト時処理関数
//ルートアクセス時の応答関数 void handleRoot()
void handleRoot() {
//digitalWrite(LED_BUILTIN, HIGH); //Android(192.168.5.18)と通信がつながった時に点灯
server.send(200, "text/Html", html); //レスポンス200を返しhtml送信
}
void TouchAndroid(){ //Android 画面 演奏/一時停止タッチ操作処理
if(PlayMode == 0)PlayMode = 1;
else PlayMode = 0;
handleRoot();
}
void setup(void) {
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(115200);
delay(50);
WiFi.mode(WIFI_AP);
WiFi.softAPConfig(ip, ip, subnet);
WiFi.softAP(ssid, pass);
delay(50);
IPAddress ipAddress = WiFi.softAPIP(); // ローカルネットワークWiFiシステム起動
Serial.print("SoftAP IP Address = "); //Androidで使用するIPアドレス表示
Serial.println(ipAddress);
server.on("/", handleRoot);//サーバー設定
server.on("/myTouch", TouchAndroid); //Android画面 Play/Pauseボタン操作 受信処理 //第1引き数の先頭に、"/"必須
//server.on("/#", Led); //<button type="button" class="example"><a href="#">Play/Pause</a></button>
//上記の場合 "#"、"/#" いずれでもLed()内の内臓LEDは点灯しない→関数LED()へ制御は達していない
server.begin();//サーバーの起動 server.begin();
Serial.println("HTTP server started");
pinMode(GpPwm, OUTPUT); //PWM 出力モードに設定
pinMode(GpLedRed, OUTPUT); //LED
pinMode(GpLedGreen, OUTPUT); //LED
pinMode(GpPlayPauseSw, INPUT_PULLUP); //プルアップ付き入力
states = SET_SOUND_FREQUENCY_TIME; //最初の音符解読ステート設定
}
void loop(void) {
server.handleClient(); //登録した情報に従ってクライアントからのリクエストを処理する関数
if(PlayMode == 1){ //再生モード
switch(states){
case SET_SOUND_FREQUENCY_TIME: //音階周波数、及びPWM音発生時間設定
OnkaiOut(Furusato[ix][0],0.5); //PWM出力開始 Fursato[][3]
interval_T = Furusato[ix][1] * 1000;
current_T = time_us_64(); //pico起動後経過の時間取得[μsec]
target_T = current_T + interval_T; //PWM終了時刻計算
states = CHECK_SOUNDING_TIME;
break;
case CHECK_SOUNDING_TIME: //PWM音終了時刻チェック
if(target_T <= time_us_64())
states = STOP_SOUNDING;
break;
case STOP_SOUNDING: //PWM音終了・無音処理 //ショート無音時間設定
analogWrite(GpPwm,0); //PWM 停止 //必須
interval_T = 50 * 1000;
current_T = time_us_64(); //pico起動後経過の時間取得[μsec]
target_T = current_T + interval_T; //ショート無音時間終了時刻計算
states = CHECH_SHORT_SILENT_TIME;
break;
case CHECH_SHORT_SILENT_TIME: //ショート無音時間終了チェック
if(target_T <= time_us_64())
states = STOP_SHORT_SILENT_TIME;
break;
case STOP_SHORT_SILENT_TIME: //音符上の休止無音時間、メロディー終了後の無音時間設定
interval_T = Furusato[ix][2] * 1000;
current_T = time_us_64(); //pico起動後経過の時間取得[μsec]
target_T = current_T + interval_T; //無音終了時刻計算
states = CHECK_SILENT_TIME;
break;
case CHECK_SILENT_TIME: //音符上の休止無音時間チェック
if(target_T <= time_us_64())
{
ix++;
if(Furusato[ix][0] == -1)ix = 0; //曲の終末を検出 → 曲の先頭から繰り返す
states = SET_SOUND_FREQUENCY_TIME; //初めに戻る
}
break;
default:
break;
}
}
SwCheck(); //ボタンスイッチチック
LedCont(); //LEDの点灯/消灯制御実施
}