Last active
January 13, 2022 06:30
-
-
Save mongonta0716/9ee8aa756ea4b74d4ea2498d4df012fa to your computer and use it in GitHub Desktop.
スタックチャン + Aquestalk LSIで初めまして( https://twitter.com/mongonta555/status/1479484496764637184 )
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Arduino.h> | |
#include "BF_AquesTalkPicoWire.h" // https://github.com/botanicfields/PCB-MBUS-AquesTalk-pico-LSI/tree/main/BF-034_Wire | |
#include "esp_adc_cal.h" // for esp_adc_cal_characteristics_t | |
#if defined(ARDUINO_M5STACK_Core2) | |
#include <M5Core2.h> | |
#define SERVO_PIN_X 13 | |
#define SERVO_PIN_Y 14 | |
#elif defined( ARDUINO_M5STACK_FIRE ) | |
#include <M5Stack.h> | |
#define SERVO_PIN_X 22 | |
#define SERVO_PIN_Y 21 | |
#elif defined( ARDUINO_M5Stack_Core_ESP32 ) | |
#include <M5Stack.h> | |
#define SERVO_PIN_X 16 | |
#define SERVO_PIN_Y 17 | |
#endif | |
#include <Avatar.h> // https://github.com/meganetaaan/m5stack-avatar | |
#include <ServoEasing.hpp> // https://github.com/ArminJo/ServoEasing | |
using namespace m5avatar; | |
Avatar avatar; | |
AquesTalkPicoWire aqtp; | |
const int fir_size(20); | |
const double fir_coef[] = | |
{ 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, }; // moving average | |
int ring_buf[fir_size]; | |
int ring_buf_head(0); | |
const int fir_out_size(30); | |
int fir_out[fir_out_size]; | |
esp_adc_cal_characteristics_t adc_chars; | |
#define START_DEGREE_VALUE_X 90 | |
#define START_DEGREE_VALUE_Y 90 | |
ServoEasing servo_x; | |
ServoEasing servo_y; | |
bool random_move = false; | |
const char* lyrics[] = { "BtnA:MoveTo90 ", "BtnB:ServoTest ", "BtnC:RandomMode "}; | |
const int lyrics_size = sizeof(lyrics) / sizeof(char*); | |
int lyrics_idx = 0; | |
void testMode() { | |
random_move = false; | |
servo_y.easeToD(50, 500); | |
servo_y.easeToD(90, 1000); | |
servo_y.easeToD(75, 1000); | |
servo_x.easeToD(70, 500); | |
servo_x.easeToD(110, 500); | |
servo_x.easeToD(90, 1000); | |
servo_y.easeToD(50, 500); | |
servo_y.easeToD(90, 500); | |
servo_y.easeToD(75, 1000); | |
} | |
void initADC() { | |
adc_power_on(); | |
adc_gpio_init(ADC_UNIT_1, ADC_CHANNEL_7); // ADC1 Channel 7 = GPIO35 | |
adc1_config_channel_atten(ADC1_CHANNEL_7, ADC_ATTEN_DB_11); // attenuation 11dB | |
adc1_config_width(ADC_WIDTH_BIT_12); // width 12bit | |
//Characterize ADC at particular ATTEN | |
const int default_vref(1100); | |
esp_adc_cal_value_t cal_value = esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, default_vref, &adc_chars); | |
//Check type of calibration value used to characterize ADC | |
String msg = "[AdcWaveInit] cal_value = "; | |
switch (cal_value) { | |
case ESP_ADC_CAL_VAL_EFUSE_VREF: msg += "eFuse Vref"; break; | |
case ESP_ADC_CAL_VAL_EFUSE_TP: msg += "Two Point"; break; | |
default: msg += "Default"; break; | |
} | |
Serial.println(msg); | |
// ring buffer and FIR | |
ring_buf_head = 0; | |
} | |
void lipsync(void *args) { | |
unsigned int start_ms = millis(); | |
for(;;) { | |
// read ADC for one frame of LCD | |
for (int i = -30; i < fir_out_size; ++i) { | |
// read ADC into ring buffer | |
if(++ring_buf_head >= fir_size) | |
ring_buf_head = 0; | |
ring_buf[ring_buf_head] = esp_adc_cal_raw_to_voltage(adc1_get_raw(ADC1_CHANNEL_7), &adc_chars); | |
// FIR filter on the ring buffer | |
double fir_sum = 0.0; | |
for (int j = 0; j < fir_size; ++j) { | |
int k = ring_buf_head - j; | |
if (k < 0) | |
k += fir_size; | |
fir_sum += fir_coef[j] * (double)ring_buf[k]; | |
} | |
// output of FIR filter | |
if (i >= 0) | |
fir_out[i] = (int)fir_sum; | |
vTaskDelay(1/portTICK_RATE_MS); | |
} | |
// statistics | |
int sum = fir_out[0]; | |
int min = fir_out[0]; | |
int max = fir_out[0]; | |
for (int i = 1; i < fir_out_size; ++i) { | |
sum += fir_out[i]; | |
min = min > fir_out[i] ? fir_out[i] : min; | |
max = max < fir_out[i] ? fir_out[i] : max; | |
} | |
int ave = sum / fir_out_size; | |
int pp = max - min; | |
int elapsed_ms = millis() - start_ms; | |
Serial.printf("[AdcWave] %dms, min %d, ave %d, max %d, pp %d\n", elapsed_ms, min, ave, max, pp); | |
float ratio = pp / 600.0f; | |
avatar.setMouthOpenRatio(ratio); | |
vTaskDelay(1/portTICK_RATE_MS); | |
} | |
} | |
void startThreads() { | |
xTaskCreateUniversal(lipsync, | |
"lipsync", | |
1024, | |
NULL, | |
6, | |
NULL, | |
tskNO_AFFINITY); | |
} | |
void setup() { | |
#if defined(ARDUINO_M5STACK_Core2) | |
M5.begin(true, true, true, false, kMBusModeOutput); | |
// M5.begin(true, true, true, false, kMBusModeInput); | |
#elif defined( ARDUINO_M5STACK_FIRE ) || defined( ARDUINO_M5Stack_Core_ESP32 ) | |
M5.begin(true, true, true, true); // Grove.Aを使う場合は第四引数(I2C)はfalse | |
#endif | |
aqtp.Begin(Wire); | |
if (servo_x.attach(SERVO_PIN_X, START_DEGREE_VALUE_X, DEFAULT_MICROSECONDS_FOR_0_DEGREE, DEFAULT_MICROSECONDS_FOR_180_DEGREE)) { | |
Serial.print("Error attaching servo x"); | |
} | |
if (servo_y.attach(SERVO_PIN_Y, START_DEGREE_VALUE_Y, DEFAULT_MICROSECONDS_FOR_0_DEGREE, DEFAULT_MICROSECONDS_FOR_180_DEGREE)) { | |
Serial.print("Error attaching servo y"); | |
} | |
servo_x.setEasingType(EASE_QUADRATIC_IN_OUT); | |
servo_y.setEasingType(EASE_QUADRATIC_IN_OUT); | |
setSpeedForAllServos(60); | |
avatar.init(); | |
initADC(); | |
aqtp.Send("#V\r"); // read version | |
for (int i = 0; i < 10; ++i) { | |
aqtp.ShowRes(); | |
delay(200); | |
} | |
aqtp.Send("#J\r"); // chime sound J | |
for (int i = 0; i < 10; ++i) { | |
aqtp.ShowRes(); | |
delay(200); | |
} | |
aqtp.Send("#K\r"); // chime sound K | |
for (int i = 0; i < 10; ++i) { | |
aqtp.ShowRes(); | |
delay(200); | |
} | |
startThreads(); | |
//aqtp.Send("sutakkuchan,bu-to,o-ke-,shisutemu,no-maru,taipuzero,sutanbai\r"); | |
aqtp.Send("hajimema'_shite watashiwa su'-pa-/kawai'i/robotto suta'xtu_ku/chan'nto/iima'_su.yoroshikune?\r"); | |
testMode(); | |
} | |
void loop() { | |
M5.update(); | |
if (M5.BtnA.wasPressed()) { | |
random_move = false; | |
servo_x.setEaseTo(90); | |
servo_y.setEaseTo(90); | |
synchronizeAllServosStartAndWaitForAllServosToStop(); | |
} | |
if (M5.BtnB.wasPressed()) { | |
for (int i=0; i<2; i++) { | |
testMode(); | |
} | |
} | |
if (M5.BtnC.wasPressed()) { | |
random_move = !random_move; | |
} | |
if (random_move) { | |
int x = random(45); | |
int y = random(40); | |
M5.update(); | |
if (M5.BtnC.wasPressed()) { | |
random_move = false; | |
} | |
servo_x.setEaseTo(x + 45); | |
servo_y.setEaseTo(y + 40); | |
synchronizeAllServosStartAndWaitForAllServosToStop(); | |
int delay_time = random(10); | |
delay(2000 + 100*delay_time); | |
avatar.setSpeechText("Stop BtnC"); | |
} | |
// if (!random_move) { | |
// const char* l = lyrics[lyrics_idx++ % lyrics_size]; | |
// avatar.setSpeechText(l); | |
// avatar.setMouthOpenRatio(0.7); | |
// delay(200); | |
// avatar.setMouthOpenRatio(0.0); | |
// delay(2000); | |
// } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment