RISC-V

2020年6月29日 (月)

Wio_Lite_RISC-VボードでWiFiを動かす(その4:MQTT)

2020/6/28:
初版

PlatformIO Wio Lite RISC-V WiFi4 MQTT

PlatformIO Wio Lite RISC-V WiFi4 MQTT

概要

Wio_Lite_RISC-VボードでWiFiを動かす(その4:MQTT)。 これは「Wio_Lite_RISC-VボードでWiFiを動かす(その3:OSC)」の続きになる。ここでは、MQTTのデモプログラムについて記載する
開発ツール(PlatformIOなど)のインストールについては「開発ツールPlatformIOをcliで使う(Wio_Lite_RISC-V版)」を参照のこと、ここでは、WiFiを動かすためのプログラムについて記載する。 (ホストPCとしてはubuntuを想定している)

プログラムにある以下の関数に関しては、 https://github.com/nopnop2002/MQTT_via_ESP01/blob/master/MQTT_Subscribe_ESP01/MQTT_Subscribe_ESP01.inoにあるものを、そのまま流用した:
・int buildConnect(char *buf, int keep_alive, char *client_id, char *will_topic, char *will_msg)
・int buildSubscribe(char *buf, char *topic)
・int buildPublish(char *buf, char *topic, char *msg)

接続

wio-lite-rvボードのPA9(TX). PA10(RX)、GNDをUSBシリアルに接続する。
ちなみに、XIAOをUSBシリアルとして使用する場合は以下のように接続する:

wio-lite-rv XIAO
PA10(RX) D6(TX)
PA9(TX) D7(RX)
GND GND

MQTT_pub_test

MQTT送信プログラム:
src/MQTT_pub_test.cpp

#include <Arduino.h> #include <stdio.h> #include <stdarg.h> #include <math.h> #define MY_SSID "your_ssid" #define MY_PASSWD "your_passwd" #define TARGET_IP "test.mosquitto.org" #define OUT_PORT 1883 #define IN_PORT 1883 char ipAddress [20]; #define CLIENT_ID "fedx" // unique string(you can use MAC address) You can change #define MQTT_KEEP_ALIVE 60 #define MQTT_WILL_TOPIC "ESP8266AT-MQTT/" // You can change #define MQTT_WILL_MSG "I am leaving..." // You can change //----- MSTT con/sub/pub func ----- char con[20]; char sub[100]; char pub[100]; int msize, plen, slen; int buildConnect(char *buf, int keep_alive, char *client_id, char *will_topic, char *will_msg) { int rlen = 12; int pos = 14; int client_id_len = strlen(client_id); //Serial.println(client_id_len); buf[pos++] = 0x00; buf[pos++] = client_id_len; for(int i=0;i<client_id_len;i++) { buf[pos++] = client_id[i]; } rlen = rlen + 2 + client_id_len; int will_topic_len = strlen(will_topic); // Serial.print("will_topic_len="); // Serial.println(will_topic_len); int will_msg_len = strlen(will_msg); // Serial.print("will_msg_len="); // Serial.println(will_msg_len); if (will_topic_len > 0 && will_msg_len > 0) { buf[pos++] = 0x00; buf[pos++] = will_topic_len; for(int i=0;i<will_topic_len;i++) { buf[pos++] = will_topic[i]; } buf[pos++] = 0x00; buf[pos++] = will_msg_len; for(int i=0;i<will_msg_len;i++) { buf[pos++] = will_msg[i]; } rlen = rlen + 2 + will_topic_len + 2 + will_msg_len; } buf[0] = 0x10; buf[1] = rlen; buf[2] = 0x00; buf[3] = 0x06; buf[4] = 'M'; buf[5] = 'Q'; buf[6] = 'I'; buf[7] = 's'; buf[8] = 'd'; buf[9] = 'p'; buf[10] = 0x03; buf[11] = 0x02; if (will_topic_len > 0 && will_msg_len > 0) buf[11] = 0x06; buf[12] = 0x00; buf[13] = keep_alive; return buf[1] + 2; } int buildSubscribe(char *buf, char *topic) { int tlen = strlen(topic); for(int i=0;i<tlen;i++) { buf[6+i] = topic[i]; } buf[0] = 0x82; buf[1] = tlen + 5; buf[2] = 0x00; buf[3] = 0x01; buf[4] = 0x00; buf[5] = tlen; buf[tlen+6] = 0x00; return buf[1] + 2; } int buildPublish(char *buf, char *topic, char *msg) { int tlen = strlen(topic); for(int i=0;i<tlen;i++) { buf[4+i] = topic[i]; } int mlen = strlen(msg); for(int i=0;i<mlen;i++) { buf[4+tlen+i] = msg[i]; } buf[0] = 0x30; buf[1] = tlen + mlen + 2; buf[2] = 0x00; buf[3] = tlen; return buf[1] + 2; } //------------------------------ static void wio_serial_init(void) { // enable GPIO clock for USART0 rcu_periph_clock_enable(RCU_GPIOA); // enable USART clock rcu_periph_clock_enable(RCU_USART0); /* connect port to USARTx_Tx(PA9) */ gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); /* connect port to USARTx_Rx(PA10) */ gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10); // enable GPIO clock for USART1 rcu_periph_clock_enable(RCU_GPIOA); // enable USART clock rcu_periph_clock_enable(RCU_USART1); /* connect port to USARTx_Tx(PA2) */ gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2); /* connect port to USARTx_Rx(PA3) */ gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_3); // USART0 configure usart_deinit(USART0); usart_baudrate_set(USART0, 115200U); usart_word_length_set(USART0, USART_WL_8BIT); usart_stop_bit_set(USART0, USART_STB_1BIT); usart_parity_config(USART0, USART_PM_NONE); usart_hardware_flow_rts_config(USART0, USART_RTS_DISABLE); usart_hardware_flow_cts_config(USART0, USART_CTS_DISABLE); usart_receive_config(USART0, USART_RECEIVE_ENABLE); usart_transmit_config(USART0, USART_TRANSMIT_ENABLE); usart_enable(USART0); // USART1 configure usart_deinit(USART1); usart_baudrate_set(USART1, 115200U); usart_word_length_set(USART1, USART_WL_8BIT); usart_stop_bit_set(USART1, USART_STB_1BIT); usart_parity_config(USART1, USART_PM_NONE); usart_hardware_flow_rts_config(USART1, USART_RTS_DISABLE); usart_hardware_flow_cts_config(USART1, USART_CTS_DISABLE); usart_receive_config(USART1, USART_RECEIVE_ENABLE); usart_transmit_config(USART1, USART_TRANSMIT_ENABLE); usart_enable(USART1); } int _get0_char(void) { while ( usart_flag_get(USART0, USART_FLAG_RBNE)== RESET){ } return (uint8_t)usart_data_receive(USART0); } int _get1_char(void) { while ( usart_flag_get(USART1, USART_FLAG_RBNE)== RESET){ } return (uint8_t)usart_data_receive(USART1); } int _put0_char(int ch) { usart_data_transmit(USART0, (uint8_t) ch ); while ( usart_flag_get(USART0, USART_FLAG_TBE)== RESET){ } return ch; } int _put1_char(int ch) { usart_data_transmit(USART1, (uint8_t) ch ); while ( usart_flag_get(USART1, USART_FLAG_TBE)== RESET){ } return ch; } void usart0_printf(const char *fmt, ...) { char buf[100]; va_list args; va_start(args, fmt); vsprintf(buf, fmt, args); va_end(args); char *p = buf; while( *p != '\0' ) { _put0_char(*p); p++; } } void usart0_print(const char *s) { while( *s != '\0' ) { _put0_char(*s); s++; } } void usart0_println(const char *s) { while( *s != '\0' ) { _put0_char(*s); s++; } _put0_char('\r'); _put0_char('\n'); } void usart1_printf(const char *fmt, ...) { char buf[100]; va_list args; va_start(args, fmt); vsprintf(buf, fmt, args); va_end(args); char *p = buf; while( *p != '\0' ) { _put1_char(*p); p++; } } void usart1_print(const char *s) { while( *s != '\0' ) { _put1_char(*s); s++; } } void usart1_println(const char *s) { while( *s != '\0' ) { _put1_char(*s); s++; } _put1_char('\r'); _put1_char('\n'); } int usart1_available(void) { return ( usart_flag_get(USART1, USART_FLAG_RBNE)== SET); } //------------------------------------------------------- char res[500]; void get_res(void) { int cpos = 0; for(int cc=0; cc<24000000; cc++) { while ( usart_flag_get(USART1, USART_FLAG_RBNE)== SET) { //res[cpos] = (char) _get1_char(); cpos++; res[cpos] = (char) usart_data_receive(USART1); cpos++; }; }; res[cpos] = 0; } void get_res2(void) { int cpos = 0; for(int cc=0; cc<500000; cc++) { while ( usart_flag_get(USART1, USART_FLAG_RBNE)== SET) { //res[cpos] = (char) _get1_char(); cpos++; res[cpos] = (char) usart_data_receive(USART1); cpos++; }; }; res[cpos] = 0; } //--------------------------------- void setup() { wio_serial_init(); //Serial.begin(115200); //serial port of GD32 //Serial1.begin(115200); //serial port of ESP8266 pinMode(LED_BUILTIN, OUTPUT); delay(3000); usart0_print("\r\n\r\n\r\n\r\n\r\n"); // rest ESP8266 usart0_println("SEND:"); usart0_print("AT+RST\r\n"); usart1_print("AT+RST\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); // get firmware version usart0_println("SEND:"); usart0_print("AT+GMR\r\n"); usart1_print("AT+GMR\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_println("SEND:"); usart0_print("AT+CWQAP\r\n"); usart1_print("AT+CWQAP\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_println("SEND:"); usart0_print("AT+CWMODE=1\r\n"); usart1_print("AT+CWMODE=1\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); //Serial1.println("AT+CWJAP=\"Your WiFi SSID\",\"Password\""); // add your own wifi usart0_println("SEND:"); usart0_print("AT+CWJAP=\""); usart0_print(MY_SSID); usart0_print("\",\""); usart0_print(MY_PASSWD); usart0_print("\"\r\n"); usart1_print("AT+CWJAP=\""); usart1_print(MY_SSID); usart1_print("\",\""); usart1_print(MY_PASSWD); usart1_print("\"\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_println("SEND:"); usart0_print("AT+CIFSR\r\n"); usart1_print("AT+CIFSR\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); //-- get IP Address -- int len = strlen( res ); bool done=false; bool error = false; int pos = 0; while (!done) { if ( res[pos] == '\"') { done = true;} pos++; if (pos > len) { done = true; error = true;} } if (!error) { int buffpos = 0; done = false; while (!done) { if ( res[pos] == '\"' ) { done = true; } else { ipAddress[buffpos] = res[pos]; buffpos++; pos++;} } ipAddress[buffpos] = 0; } else { strcpy(ipAddress,"ERROR"); } //usart0_printf("ipAddress:%s\r\n",ipAddress); //----------------- usart0_println("SEND:"); usart0_print("AT+CIPMUX=1\r\n"); usart1_print("AT+CIPMUX=1\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_println("SEND:"); usart0_print("AT+CIPSERVER=1,80\r\n"); ; usart1_print("AT+CIPSERVER=1,80\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_printf("\r\nipAddress: %s\r\n\r\n",ipAddress); //------------------------------------------ // TCP access usart0_printf("AT+CIPSTART=1,\"TCP\",\"%s\",%d\r\n",TARGET_IP,OUT_PORT); usart1_printf("AT+CIPSTART=1,\"TCP\",\"%s\",%d\r\n",TARGET_IP,OUT_PORT); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); // build connect info msize = buildConnect(con,MQTT_KEEP_ALIVE,CLIENT_ID,MQTT_WILL_TOPIC,MQTT_WILL_MSG); // send connect info usart0_printf("AT+CIPSEND=1,%d\r\n",msize); usart1_printf("AT+CIPSEND=1,%d\r\n",msize); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); // send connect info for (int i=0;i<msize;i++) _put1_char(con[i]); for (int i=0;i<msize;i++) _put0_char(con[i]); _put0_char('\r'); _put0_char('\n'); get_res(); } //------------ void dispatch(void) { // setup string pointer table char *h[20]; for(int i=0; i<20; i++) h[i]=NULL; h[0] = &res[0]; int pi = 1; int cp; int len = (int)strlen(res); // keep length of res[] for (int i=0; i<len; i++) { //if ((res[i]=='I') && (res[i+1]=='P') && (res[i+2]=='D') ) { // check IPD if ((res[i]=='+') && (res[i+1]=='I') && (res[i+2]=='P') && (res[i+3]=='D') ) { // check +IPD //for (cp = i+3; cp<len; cp++) { for (cp = i+4; cp<len; cp++) { if (res[cp]==':') break; } res[i] = 0; // make string terminator h[pi] = &res[cp+1]; pi++; } }; // display removed IPD header for(int i=0; i<20; i++) { if (h[i] != NULL) usart0_print(h[i]); } } //------------ void loop() { digitalWrite(LED_BUILTIN, HIGH); // debug: for indicate in-loop /** // test loop while(true) { if ( usart_flag_get(USART1, USART_FLAG_RBNE)== SET) _put0_char(_get1_char()); if ( usart_flag_get(USART0, USART_FLAG_RBNE)== SET) _put1_char(_get0_char()); }; **/ while(true) { get_res2(); if ((int)strlen(res) != 0) { //usart0_print("\r\nRES:\r\n"); //usart0_println(res); dispatch(); } else { //usart0_print("."); // indicating input wait // publishing // message#0 plen = buildPublish(pub, "topic0", "msg-hello00"); usart0_printf("AT+CIPSEND=1,%d\r\n",plen); usart1_printf("AT+CIPSEND=1,%d\r\n",plen); get_res(); // send pub info for (int i=0;i<plen;i++) _put1_char(pub[i]); get_res(); usart0_println("pub-res:"); usart0_println(res); usart0_println("-----------"); // message#1 plen = buildPublish(pub, "topic0", "msg-hello01"); usart0_printf("AT+CIPSEND=1,%d\r\n",plen); usart1_printf("AT+CIPSEND=1,%d\r\n",plen); get_res(); // send pub info for (int i=0;i<plen;i++) _put1_char(pub[i]); get_res(); usart0_println("pub-res:"); usart0_println(res); usart0_println("-----------"); // message#2 plen = buildPublish(pub, "topic0", "msg-hello02"); usart0_printf("AT+CIPSEND=1,%d\r\n",plen); usart1_printf("AT+CIPSEND=1,%d\r\n",plen); get_res(); // send pub info for (int i=0;i<plen;i++) _put1_char(pub[i]); get_res(); usart0_println("pub-res:"); usart0_println(res); usart0_println("-----------"); } } }

以下については、自分の環境に合わせて変更すること:

#define MY_SSID "your_ssid" #define MY_PASSWD "your_passwd"

以下は利用するMQTTサーバー(broker)に合わせる:

#define TARGET_IP "test.mosquitto.org" #define OUT_PORT 1883 #define IN_PORT 1883

書き込み後に「picocom /dev/ttyACM0 -b115200」で通信ソフトを起動すると以下のような出力が表示される:

$ picocom /dev/ttyACM0 -b115200 SEND: AT+RST RES: AT+RST OK I (2989) wifi: state: 5 -> 0 (0) WIFI DISCONNECT sl ----------- SEND: AT+GMR RES: AT+GMR AT version:2.1.0.0-dev(d25f6d2 - Oct 15 2019 12:03:04) SDK version:v3.2-192-g81655f39 compile time(525fbfe):Oct 17 2019 05:39:13 Bin version:2.0.0(WROOM-02) OK ... <省略> ... ----------- ipAddress: 192.168.0.18 AT+CIPSTART=1,"TCP","test.mosquitto.org",1883 RES: AT+CIPSTART=1,"TCP","test.mosquitto.org",1883 1,CONNECT OK ... <省略>

対向する送受信プログラム

以下をブラウザ(chromeのみ)で動かす:

MQTT_AT_webTest.html

<html> <script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script> <body> <script> document.body.innerHTML = ''; var consout = 'MQTT over WebSockets Test'+'<br>' document.body.innerHTML = consout; //var mqtt = require('mqtt') var client = mqtt.connect('ws://test.mosquitto.org:8081') // subscribe Topic client.subscribe('topic0'); //client.subscribe('topic0/sample/hello'); //client.subscribe('topic0/sample/#'); // wildcard topic //client.subscribe('topic0/#'); //client.subscribe('#'); client.on('message', function(topic, payload) { console.log([topic, payload].join(': ')); consout += [topic, payload].join(': ')+'<br>' document.body.innerHTML = consout // disconnect //client.end(); }); // publish messages client.publish('topic0', 'hello world of MQTT! #1'); client.publish('topic0', 'hello world of MQTT! #2'); client.publish('topic0', 'hello world of MQTT! #3'); client.publish('topic0', 'hello world of MQTT! #4'); client.publish('topic0', 'hello world of MQTT! #5'); </script> </body> </html>

上をプラウザーで起動した後、 wio-liteの送信プログラムを動作させる。

以下のweb画面が表示される(例):

MQTT over WebSockets Test topic0: Bienvenue sur mosquitto.org topic0: hello world of MQTT! #1   # webアプリで送信(publish)したものを受信している topic0: hello world of MQTT! #2   # webアプリで送信(publish)したものを受信している topic0: hello world of MQTT! #3   # webアプリで送信(publish)したものを受信している topic0: hello world of MQTT! #4   # webアプリで送信(publish)したものを受信している topic0: hello world of MQTT! #5   # webアプリで送信(publish)したものを受信している topic0: msg-hello00 # wio-liteで送信(publish)したものを受信している topic0: msg-hello01 # wio-liteで送信(publish)したものを受信している topic0: msg-hello02 # wio-liteで送信(publish)したものを受信している topic0: msg-hello00 # wio-liteで送信(publish)したものを受信している topic0: msg-hello01 # wio-liteで送信(publish)したものを受信している topic0: msg-hello02 # wio-liteで送信(publish)したものを受信している topic0: msg-hello00 # wio-liteで送信(publish)したものを受信している topic0: msg-hello01 # wio-liteで送信(publish)したものを受信している topic0: msg-hello02 # wio-liteで送信(publish)したものを受信している topic0: msg-hello00 # wio-liteで送信(publish)したものを受信している topic0: msg-hello01 # wio-liteで送信(publish)したものを受信している topic0: msg-hello02 # wio-liteで送信(publish)したものを受信している ...

MQTT_sub_test

MQTT受信プログラム:
src/MQTT_sub_test.cpp

#include <Arduino.h> #include <stdio.h> #include <stdarg.h> #include <math.h> #define MY_SSID "your_ssid" #define MY_PASSWD "your_passswd" #define TARGET_IP "test.mosquitto.org" #define OUT_PORT 1883 #define IN_PORT 1883 char ipAddress [20]; #define CLIENT_ID "fedx" // unique string(you can use MAC address) You can change #define MQTT_KEEP_ALIVE 3600 // 60 #define MQTT_WILL_TOPIC "ESP8266AT-MQTT/" // You can change #define MQTT_WILL_MSG "I am leaving..." // You can change //----- MSTT con/sub/pub func ----- char con[20]; char sub[100]; char pub[100]; int msize, plen, slen; int buildConnect(char *buf, int keep_alive, char *client_id, char *will_topic, char *will_msg) { int rlen = 12; int pos = 14; int client_id_len = strlen(client_id); //Serial.println(client_id_len); buf[pos++] = 0x00; buf[pos++] = client_id_len; for(int i=0;i<client_id_len;i++) { buf[pos++] = client_id[i]; } rlen = rlen + 2 + client_id_len; int will_topic_len = strlen(will_topic); // Serial.print("will_topic_len="); // Serial.println(will_topic_len); int will_msg_len = strlen(will_msg); // Serial.print("will_msg_len="); // Serial.println(will_msg_len); if (will_topic_len > 0 && will_msg_len > 0) { buf[pos++] = 0x00; buf[pos++] = will_topic_len; for(int i=0;i<will_topic_len;i++) { buf[pos++] = will_topic[i]; } buf[pos++] = 0x00; buf[pos++] = will_msg_len; for(int i=0;i<will_msg_len;i++) { buf[pos++] = will_msg[i]; } rlen = rlen + 2 + will_topic_len + 2 + will_msg_len; } buf[0] = 0x10; buf[1] = rlen; buf[2] = 0x00; buf[3] = 0x06; buf[4] = 'M'; buf[5] = 'Q'; buf[6] = 'I'; buf[7] = 's'; buf[8] = 'd'; buf[9] = 'p'; buf[10] = 0x03; buf[11] = 0x02; if (will_topic_len > 0 && will_msg_len > 0) buf[11] = 0x06; /* buf[12] = 0x00; buf[13] = keep_alive; */ buf[12] = 0xff & (keep_alive >> 8); buf[13] = 0xff & keep_alive; return buf[1] + 2; } int buildSubscribe(char *buf, char *topic) { int tlen = strlen(topic); for(int i=0;i<tlen;i++) { buf[6+i] = topic[i]; } buf[0] = 0x82; buf[1] = tlen + 5; buf[2] = 0x00; buf[3] = 0x01; buf[4] = 0x00; buf[5] = tlen; buf[tlen+6] = 0x00; return buf[1] + 2; } int buildPublish(char *buf, char *topic, char *msg) { int tlen = strlen(topic); for(int i=0;i<tlen;i++) { buf[4+i] = topic[i]; } int mlen = strlen(msg); for(int i=0;i<mlen;i++) { buf[4+tlen+i] = msg[i]; } buf[0] = 0x30; buf[1] = tlen + mlen + 2; buf[2] = 0x00; buf[3] = tlen; return buf[1] + 2; } //------------------------------ static void wio_serial_init(void) { // enable GPIO clock for USART0 rcu_periph_clock_enable(RCU_GPIOA); // enable USART clock rcu_periph_clock_enable(RCU_USART0); /* connect port to USARTx_Tx(PA9) */ gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); /* connect port to USARTx_Rx(PA10) */ gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10); // enable GPIO clock for USART1 rcu_periph_clock_enable(RCU_GPIOA); // enable USART clock rcu_periph_clock_enable(RCU_USART1); /* connect port to USARTx_Tx(PA2) */ gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2); /* connect port to USARTx_Rx(PA3) */ gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_3); // USART0 configure usart_deinit(USART0); usart_baudrate_set(USART0, 115200U); usart_word_length_set(USART0, USART_WL_8BIT); usart_stop_bit_set(USART0, USART_STB_1BIT); usart_parity_config(USART0, USART_PM_NONE); usart_hardware_flow_rts_config(USART0, USART_RTS_DISABLE); usart_hardware_flow_cts_config(USART0, USART_CTS_DISABLE); usart_receive_config(USART0, USART_RECEIVE_ENABLE); usart_transmit_config(USART0, USART_TRANSMIT_ENABLE); usart_enable(USART0); // USART1 configure usart_deinit(USART1); usart_baudrate_set(USART1, 115200U); usart_word_length_set(USART1, USART_WL_8BIT); usart_stop_bit_set(USART1, USART_STB_1BIT); usart_parity_config(USART1, USART_PM_NONE); usart_hardware_flow_rts_config(USART1, USART_RTS_DISABLE); usart_hardware_flow_cts_config(USART1, USART_CTS_DISABLE); usart_receive_config(USART1, USART_RECEIVE_ENABLE); usart_transmit_config(USART1, USART_TRANSMIT_ENABLE); usart_enable(USART1); } int _get0_char(void) { while ( usart_flag_get(USART0, USART_FLAG_RBNE)== RESET){ } return (uint8_t)usart_data_receive(USART0); } int _get1_char(void) { while ( usart_flag_get(USART1, USART_FLAG_RBNE)== RESET){ } return (uint8_t)usart_data_receive(USART1); } int _put0_char(int ch) { usart_data_transmit(USART0, (uint8_t) ch ); while ( usart_flag_get(USART0, USART_FLAG_TBE)== RESET){ } return ch; } int _put1_char(int ch) { usart_data_transmit(USART1, (uint8_t) ch ); while ( usart_flag_get(USART1, USART_FLAG_TBE)== RESET){ } return ch; } void usart0_printf(const char *fmt, ...) { char buf[100]; va_list args; va_start(args, fmt); vsprintf(buf, fmt, args); va_end(args); char *p = buf; while( *p != '\0' ) { _put0_char(*p); p++; } } void usart0_print(const char *s) { while( *s != '\0' ) { _put0_char(*s); s++; } } void usart0_println(const char *s) { while( *s != '\0' ) { _put0_char(*s); s++; } _put0_char('\r'); _put0_char('\n'); } void usart1_printf(const char *fmt, ...) { char buf[100]; va_list args; va_start(args, fmt); vsprintf(buf, fmt, args); va_end(args); char *p = buf; while( *p != '\0' ) { _put1_char(*p); p++; } } void usart1_print(const char *s) { while( *s != '\0' ) { _put1_char(*s); s++; } } void usart1_println(const char *s) { while( *s != '\0' ) { _put1_char(*s); s++; } _put1_char('\r'); _put1_char('\n'); } int usart1_available(void) { return ( usart_flag_get(USART1, USART_FLAG_RBNE)== SET); } //------------------------------------------------------- char res[500]; int reslen; // received char number for res[] void get_res(void) { int cpos = 0; for(int cc=0; cc<24000000; cc++) { while ( usart_flag_get(USART1, USART_FLAG_RBNE)== SET) { //res[cpos] = (char) _get1_char(); cpos++; res[cpos] = (char) usart_data_receive(USART1); cpos++; }; }; res[cpos] = 0; reslen = cpos; // save # of received chars } void get_res2(void) { int cpos = 0; for(int cc=0; cc<500000; cc++) { while ( usart_flag_get(USART1, USART_FLAG_RBNE)== SET) { //res[cpos] = (char) _get1_char(); cpos++; res[cpos] = (char) usart_data_receive(USART1); cpos++; }; }; res[cpos] = 0; reslen = cpos; // save # of received chars } //--------------------------------- void setup() { wio_serial_init(); //Serial.begin(115200); //serial port of GD32 //Serial1.begin(115200); //serial port of ESP8266 pinMode(LED_BUILTIN, OUTPUT); delay(3000); usart0_print("\r\n\r\n\r\n\r\n\r\n"); // rest ESP8266 usart0_println("SEND:"); usart0_print("AT+RST\r\n"); usart1_print("AT+RST\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); // get firmware version usart0_println("SEND:"); usart0_print("AT+GMR\r\n"); usart1_print("AT+GMR\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_println("SEND:"); usart0_print("AT+CWQAP\r\n"); usart1_print("AT+CWQAP\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_println("SEND:"); usart0_print("AT+CWMODE=1\r\n"); usart1_print("AT+CWMODE=1\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); //Serial1.println("AT+CWJAP=\"Your WiFi SSID\",\"Password\""); // add your own wifi usart0_println("SEND:"); usart0_print("AT+CWJAP=\""); usart0_print(MY_SSID); usart0_print("\",\""); usart0_print(MY_PASSWD); usart0_print("\"\r\n"); usart1_print("AT+CWJAP=\""); usart1_print(MY_SSID); usart1_print("\",\""); usart1_print(MY_PASSWD); usart1_print("\"\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_println("SEND:"); usart0_print("AT+CIFSR\r\n"); usart1_print("AT+CIFSR\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); //-- get IP Address -- int len = strlen( res ); bool done=false; bool error = false; int pos = 0; while (!done) { if ( res[pos] == '\"') { done = true;} pos++; if (pos > len) { done = true; error = true;} } if (!error) { int buffpos = 0; done = false; while (!done) { if ( res[pos] == '\"' ) { done = true; } else { ipAddress[buffpos] = res[pos]; buffpos++; pos++;} } ipAddress[buffpos] = 0; } else { strcpy(ipAddress,"ERROR"); } //usart0_printf("ipAddress:%s\r\n",ipAddress); //----------------- usart0_println("SEND:"); usart0_print("AT+CIPMUX=1\r\n"); usart1_print("AT+CIPMUX=1\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_println("SEND:"); usart0_print("AT+CIPSERVER=1,80\r\n"); ; usart1_print("AT+CIPSERVER=1,80\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_printf("\r\nipAddress: %s\r\n\r\n",ipAddress); //------------------------------------------ // TCP access usart0_printf("AT+CIPSTART=1,\"TCP\",\"%s\",%d\r\n",TARGET_IP,OUT_PORT); usart1_printf("AT+CIPSTART=1,\"TCP\",\"%s\",%d\r\n",TARGET_IP,OUT_PORT); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); // build connect info msize = buildConnect(con,MQTT_KEEP_ALIVE,CLIENT_ID,MQTT_WILL_TOPIC,MQTT_WILL_MSG); // send connect info usart0_printf("AT+CIPSEND=1,%d\r\n",msize); usart1_printf("AT+CIPSEND=1,%d\r\n",msize); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); // send connect info for (int i=0;i<msize;i++) _put1_char(con[i]); for (int i=0;i<msize;i++) _put0_char(con[i]); _put0_char('\r'); _put0_char('\n'); get_res(); // send sub info slen = buildSubscribe(sub, "topic0"); usart0_printf("AT+CIPSEND=1,%d\r\n",slen); usart1_printf("AT+CIPSEND=1,%d\r\n",slen); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); // send connect info for (int i=0;i<slen;i++) _put1_char(sub[i]); get_res(); } //------------ void dispatch(void) { char ph0,ph1,ph2,ph3; char rtopic[20],rmsg[50]; int pd, ps; // payload dest/src index int cp; //int len = (int)strlen(res); // keep length of res[] int len = reslen; // get # of received chars for (int i=0; i<len; i++) { if ((res[i]=='+') && (res[i+1]=='I') && (res[i+2]=='P') && (res[i+3]=='D') ) { // check +IPD for (cp = i+4; cp<len; cp++) { if (res[cp]==':') break; } ph0 = res[cp+1]; // ph0 (Payload Header 0) ph1 = res[cp+2]; // topic_length + msg_length +2 ph2 = res[cp+3]; ph3 = res[cp+4]; // topic_length //usart0_printf("ph0:0x%x ph2:0x%x\r\n",(int)ph0,(int)ph2); // for debug //usart0_printf("ph1:%d ph3:%d\r\n",(int)ph1,(int)ph3); // for debug ps = cp+5; for(pd=0; pd<ph3; pd++) {rtopic[pd]=res[ps]; ps++; }; rtopic[pd] = 0; for(pd=0; pd<(ph1-ph3-2); pd++) {rmsg[pd]=res[ps]; ps++; }; rmsg[pd] = 0; // display received topic,message usart0_printf("received %s: %s\r\n",rtopic,rmsg); } }; } //------------ void loop() { digitalWrite(LED_BUILTIN, HIGH); // debug: for indicate in-loop /** // test loop while(true) { if ( usart_flag_get(USART1, USART_FLAG_RBNE)== SET) _put0_char(_get1_char()); if ( usart_flag_get(USART0, USART_FLAG_RBNE)== SET) _put1_char(_get0_char()); }; **/ while(true) { get_res2(); //if ((int)strlen(res) != 0) { if (reslen != 0) { //usart0_print("\r\nRES:\r\n"); //usart0_println(res); dispatch(); } else { //usart0_print("."); // indicating input wait } } }

「MQTT_AT_webTest.html」をプラウザで起動した後、 wio-liteの受信プログラムを動作させる。

書き込み後に「picocom /dev/ttyACM0 -b115200」で通信ソフトを起動すると以下のような出力が表示される:

$ picocom /dev/ttyACM0 -b115200 SEND: AT+RST RES: AT+RST OK I (2989) wifi: state: 5 -> 0 (0) WIFI DISCONNECT sl ----------- SEND: AT+GMR RES: AT+GMR AT version:2.1.0.0-dev(d25f6d2 - Oct 15 2019 12:03:04) SDK version:v3.2-192-g81655f39 compile time(525fbfe):Oct 17 2019 05:39:13 Bin version:2.0.0(WROOM-02) OK ... <省略> OK #以下、ブラウザーをリフレッシュ(リロード)するたびにpublishされ #受信したものが以下のように表示される: > ----------- received topic0: hello world of MQTT! #1 received topic0: hello world of MQTT! #2 received topic0: hello world of MQTT! #3 received topic0: hello world of MQTT! #4 received topic0: hello world of MQTT! #5 received topic0: hello world of MQTT! #1 received topic0: hello world of MQTT! #2 received topic0: hello world of MQTT! #3 received topic0: hello world of MQTT! #4 received topic0: hello world of MQTT! #5 received topic0: hello world of MQTT! #1 received topic0: hello world of MQTT! #2 received topic0: hello world of MQTT! #3 received topic0: hello world of MQTT! #4 received topic0: hello world of MQTT! #5 received topic0: hello world of MQTT! #1 received topic0: hello world of MQTT! #2 received topic0: hello world of MQTT! #3 received topic0: hello world of MQTT! #4 received topic0: hello world of MQTT! #5 received topic0: hello world of MQTT! #1 received topic0: hello world of MQTT! #2 received topic0: hello world of MQTT! #3 received topic0: hello world of MQTT! #4 received topic0: hello world of MQTT! #5 ... ...

参考情報

MQTT test server @mosquitto.org

https://test.mosquitto.org/ The server listens on the following ports: 1883 : MQTT, unencrypted 8883 : MQTT, encrypted 8884 : MQTT, encrypted, client certificate required 8080 : MQTT over WebSockets, unencrypted 8081 : MQTT over WebSockets, encrypted websocketで使用するurlは以下になる: 'ws://test.mosquitto.org:8081'

MQTT test server @eclipse.org

http://mqtt.eclipse.org/ This is a public test MQTT broker service. It currently listens on the following ports: 1883 : MQTT over unencrypted TCP 8883 : MQTT over encrypted TCP 80 : MQTT over unencrypted WebSockets (note: URL must be /mqtt ) 443 : MQTT over encrypted WebSockets (note: URL must be /mqtt ) websocketで使用するurlは以下になる: 'ws://mqtt.eclipse.org/mqtt:443'

10 Free Public & Private MQTT Brokers(For Testing & Production)

Wio Lite RISC-V(GD32VF103)

Wio Lite RISC V GD32VF103 with ESP8266
Blink with a Wio Lite RISC-V with ESP8266

PlatformIO Core (CLI)

ESP8266をTCPサーバとTCPクライアントにするATコマンド実例 (1/4)
esp8266_at_command_examples_en.pdf

ESP8266をWifiモデムとして使う - ATコマンドによるMQTT通信
MQTT_via_ESP01
Arduino WiFi library for ESP8266 modules

espressif/ESP8266_AT - examples
Espressif Documentation

以上

続きを読む "Wio_Lite_RISC-VボードでWiFiを動かす(その4:MQTT)"

| | コメント (0)

2020年6月23日 (火)

Wio_Lite_RISC-VボードでWiFiを動かす(その3:OSC)

2020/6/23:
初版

PlatformIO Wio Lite RISC-V WiFi3

PlatformIO Wio Lite RISC-V WiFi3

概要

Wio_Lite_RISC-VボードでWiFiを動かす(その3:OSC)。 これは「Wio_Lite_RISC-VボードでWiFiを動かす(その2)」の続きになる。ここでは、OSC(Open Sound Control)のデモプログラムについて記載する
開発ツール(PlatformIOなど)のインストールについては「開発ツールPlatformIOをcliで使う(Wio_Lite_RISC-V版)」を参照のこと、ここでは、WiFiを動かすためのプログラムについて記載する。 (ホストPCとしてはubuntuを想定している)

接続

wio-lite-rvボードのPA9(TX). PA10(RX)、GNDをUSBシリアルに接続する。
ちなみに、XIAOをUSBシリアルとして使用する場合は以下のように接続する:

wio-lite-rv XIAO
PA10(RX) D6(TX)
PA9(TX) D7(RX)
GND GND

OSCcodecライブラリ

OSC_codecライブラリとしてプロジェクトのsrcディレクトリに以下の2つのファイルを置く:

src/OSCmsgCodec.h

// OSC message Codec Header // 2013/10/28 #ifndef _OSCMSGCODEC_H #define _OSCMSGCODEC_H #include <string.h> int encOSCmsg(char *packet , union OSCarg *msg); // makes packet from OSC message and returns packet size void decOSCmsg(char *packet , union OSCarg *msg); // makes OSC message from packet union OSCarg { // char*, int and float are assumed four bytes char *address; char *typeTag; long int i; // int32 for Arduino(16bits) float f; char *s; struct { long int len; // is "int i" char *p; } blob; char m[4]; // for MIDI char _b[4]; // endian conversion temp variable }; #endif // _OSCMSGCODEC_H

src/OSCmsgCodec.cpp

/* <---------------------------------------------------------------------------------- OSC message Codec(encoder/decoder) version: 1.3 (2014/ 8/31) encoder bufix(enoceder returns byte length) version: 1.2 (2014/ 8/30) decoder bufix version: 1.1 (2013/11/20) support BIG_ENDIAN by #define version: 1.0 (2013/11/10) Copyright (C) 2011,2013,2014 S.Komatsu released under the MIT License: http://mbed.org/license/mit please refer to: http://opensoundcontrol.org/introduction-osc for OSC(Open Sound Control) The followings are supported: Features: Packet Parsing (Client) Packet Construction (Server) Bundle NOT Support Timetag NOT Support Type Support: i: int32 b: blob s: string f: float32 m: MIDI message(port id, status byte, data1, data2) // I don't know the detail Change Log: Bug(string length is not correct in encoding) Fix on 2013/11/10 (v0.9 -> v1.0) >---------------------------------------------------------------------------------- */ #include "OSCmsgCodec.h" //#define BIG_ENDIAN int lenAlign4B(int len) { if ((len % 4) == 0) {return len; } else {return len+4-(len % 4);} } int encOSCmsg(char *packet , union OSCarg *msg){ // *** important notice *** // output buffer must be cleared before call this char *p, *s, *d, *typeTag; char c; p=packet; d=p; s=msg[0].address; // address for(int i=0; i<strlen(msg[0].address); i++) *d++ = *s++; *d=0; // terminator // p += 4*((strlen(msg[0].address)+1)/4+1); p += lenAlign4B(strlen(msg[0].address)+1); // s=msg[1].typeTag; d=p; for(int i=0; i<strlen(msg[1].typeTag); i++) *d++ = *s++; *d=0; // terminator // p += 4*((strlen(msg[1].s)+1)/4+1); p += lenAlign4B(strlen(msg[1].typeTag)+1); // typeTag=msg[1].s+1; // skip ',' for(int n=0; n<strlen(typeTag); n++){ c = typeTag[n]; if (('s'==c)) { s=msg[n+2].s; d=p; for(int i=0; i<strlen(msg[n+2].s); i++) *d++ = *s++; *d=0; // terminater // p += 4*((strlen(msg[n+2].s)+1)/4+1); p += lenAlign4B(strlen(msg[n+2].s)+1); } else if (('i'==c)||('f'==c)) { #ifdef BIG_ENDIAN // no change endian (big to big) p[0]=msg[n+2]._b[0]; p[1]=msg[n+2]._b[1]; p[2]=msg[n+2]._b[2]; p[3]=msg[n+2]._b[3]; #else // change endian (little to big) p[0]=msg[n+2]._b[3]; p[1]=msg[n+2]._b[2]; p[2]=msg[n+2]._b[1]; p[3]=msg[n+2]._b[0]; #endif p +=4; } else if ('b'==c) { // put length of blog #ifdef BIG_ENDIAN // no change endian (big to big) p[0]=msg[n+2]._b[0]; p[1]=msg[n+2]._b[1]; p[2]=msg[n+2]._b[2]; p[3]=msg[n+2]._b[3]; #else // change endian (little to big) p[0]=msg[n+2]._b[3]; p[1]=msg[n+2]._b[2]; p[2]=msg[n+2]._b[1]; p[3]=msg[n+2]._b[0]; #endif p +=4; // get ponter of blog (copy to msg[n].blog.p) s=msg[n+2].blob.p; d=p; for(int i=0; i<msg[n+2].blob.len; i++) *d++ = *s++; p += 4*(msg[n+2].blob.len/4+1); } else if ('m'==c) { // get midi data (copy to msg[n].m[]) p[0]=msg[n+2].m[0]; p[1]=msg[n+2].m[1]; p[2]=msg[n+2].m[2]; p[3]=msg[n+2].m[3]; p +=4; } else { //printf("*** Not Supported TypeTag:%s ****\n",typeTag); } }; //return (p-packet); // return packet size // bugfix 2014/8/31 return sizeof(char)*(p-packet); // return byte length }; void decOSCmsg(char *packet , union OSCarg *msg){ // Caution: the returned result points to packet as blobs or strings (not newly allocatd) char *p, *typeTag; char c; int n; msg[0].address = packet; // address msg[1].typeTag = packet+4*((strlen(msg[0].s)+1)/4+1);//typeTag typeTag=msg[1].s+1; // skip ',' // bugfix 2014/8/30 if (strlen(typeTag)%2 == 0) p= msg[1].s+4*((strlen(msg[1].s)+1)/4); else p= msg[1].s+4*((strlen(msg[1].s)+1)/4+1); for(n=0; n<strlen(typeTag); n++){ c = typeTag[n]; if (('s'==c)) { msg[n+2].s=p; //p += 4*((strlen(msg[n+2].s)+1)/4+1); p += lenAlign4B(strlen(msg[n+2].s)+1); } else if (('i'==c)||('f'==c)) { #ifdef BIG_ENDIAN // no change endian (big to big) msg[n+2]._b[0]=p[0]; msg[n+2]._b[1]=p[1]; msg[n+2]._b[2]=p[2]; msg[n+2]._b[3]=p[3]; #else // change endian (big to little) msg[n+2]._b[3]=p[0]; msg[n+2]._b[2]=p[1]; msg[n+2]._b[1]=p[2]; msg[n+2]._b[0]=p[3]; #endif p +=4; } else if ('b'==c) { // get lenth of blog (copy to msg[n].blog.len) #ifdef BIG_ENDIAN // no change endian (big to big) msg[n+2]._b[0]=p[0]; msg[n+2]._b[1]=p[1]; msg[n+2]._b[2]=p[2]; msg[n+2]._b[3]=p[3]; #else // change endian (big to little) msg[n+2]._b[3]=p[0]; msg[n+2]._b[2]=p[1]; msg[n+2]._b[1]=p[2]; msg[n+2]._b[0]=p[3]; #endif p +=4; // get ponter of blog (copy to msg[n].blog.p) msg[n+2].blob.p=p; //p += 4*(msg[n+2].blob.len/4+1); p += lenAlign4B(msg[n+2].blob.len+1); } else if ('m'==c) { // get midi data (copy to msg[n].m[]) msg[n+2].m[0]=p[0]; msg[n+2].m[1]=p[1]; msg[n+2].m[2]=p[2]; msg[n+2].m[3]=p[3]; p +=4; } else { //printf("*** Not Supported TypeTag:%s ****\n",typeTag); } }; };

OSC_recv_test

OSC受信プログラム:
src/OSC_recv_test.cpp

#include <Arduino.h> #include <stdio.h> #include <stdarg.h> #include <math.h> // OSC related #include "OSCmsgCodec.h" #define PACKET_SIZE 512 #define RECBUF_SIZE 256 union OSCarg msg[10], outmsg[10]; char buff[PACKET_SIZE],packet[PACKET_SIZE]; #define MY_SSID "your_ssid" #define MY_PASSWD "your_passwd" #define TARGET_IP "192.168.0.23" #define OUT_PORT 9000 #define IN_PORT 8000 char ipAddress [20]; static void wio_serial_init(void) { // enable GPIO clock for USART0 rcu_periph_clock_enable(RCU_GPIOA); // enable USART clock rcu_periph_clock_enable(RCU_USART0); /* connect port to USARTx_Tx(PA9) */ gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); /* connect port to USARTx_Rx(PA10) */ gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10); // enable GPIO clock for USART1 rcu_periph_clock_enable(RCU_GPIOA); // enable USART clock rcu_periph_clock_enable(RCU_USART1); /* connect port to USARTx_Tx(PA2) */ gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2); /* connect port to USARTx_Rx(PA3) */ gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_3); // USART0 configure usart_deinit(USART0); usart_baudrate_set(USART0, 115200U); usart_word_length_set(USART0, USART_WL_8BIT); usart_stop_bit_set(USART0, USART_STB_1BIT); usart_parity_config(USART0, USART_PM_NONE); usart_hardware_flow_rts_config(USART0, USART_RTS_DISABLE); usart_hardware_flow_cts_config(USART0, USART_CTS_DISABLE); usart_receive_config(USART0, USART_RECEIVE_ENABLE); usart_transmit_config(USART0, USART_TRANSMIT_ENABLE); usart_enable(USART0); // USART1 configure usart_deinit(USART1); usart_baudrate_set(USART1, 115200U); usart_word_length_set(USART1, USART_WL_8BIT); usart_stop_bit_set(USART1, USART_STB_1BIT); usart_parity_config(USART1, USART_PM_NONE); usart_hardware_flow_rts_config(USART1, USART_RTS_DISABLE); usart_hardware_flow_cts_config(USART1, USART_CTS_DISABLE); usart_receive_config(USART1, USART_RECEIVE_ENABLE); usart_transmit_config(USART1, USART_TRANSMIT_ENABLE); usart_enable(USART1); } int _get0_char(void) { while ( usart_flag_get(USART0, USART_FLAG_RBNE)== RESET){ } return (uint8_t)usart_data_receive(USART0); } int _get1_char(void) { while ( usart_flag_get(USART1, USART_FLAG_RBNE)== RESET){ } return (uint8_t)usart_data_receive(USART1); } int _put0_char(int ch) { usart_data_transmit(USART0, (uint8_t) ch ); while ( usart_flag_get(USART0, USART_FLAG_TBE)== RESET){ } return ch; } int _put1_char(int ch) { usart_data_transmit(USART1, (uint8_t) ch ); while ( usart_flag_get(USART1, USART_FLAG_TBE)== RESET){ } return ch; } void usart0_printf(const char *fmt, ...) { char buf[100]; va_list args; va_start(args, fmt); vsprintf(buf, fmt, args); va_end(args); char *p = buf; while( *p != '\0' ) { _put0_char(*p); p++; } } void usart0_print(const char *s) { while( *s != '\0' ) { _put0_char(*s); s++; } } void usart0_println(const char *s) { while( *s != '\0' ) { _put0_char(*s); s++; } _put0_char('\r'); _put0_char('\n'); } void usart1_printf(const char *fmt, ...) { char buf[100]; va_list args; va_start(args, fmt); vsprintf(buf, fmt, args); va_end(args); char *p = buf; while( *p != '\0' ) { _put1_char(*p); p++; } } void usart1_print(const char *s) { while( *s != '\0' ) { _put1_char(*s); s++; } } void usart1_println(const char *s) { while( *s != '\0' ) { _put1_char(*s); s++; } _put1_char('\r'); _put1_char('\n'); } int usart1_available(void) { return ( usart_flag_get(USART1, USART_FLAG_RBNE)== SET); } //------------------------------------------------------- char res[500]; void get_res(void) { int cpos = 0; for(int cc=0; cc<24000000; cc++) { while ( usart_flag_get(USART1, USART_FLAG_RBNE)== SET) { //res[cpos] = (char) _get1_char(); cpos++; res[cpos] = (char) usart_data_receive(USART1); cpos++; }; }; res[cpos] = 0; } void get_res2(void) { int cpos = 0; for(int cc=0; cc<500000; cc++) { while ( usart_flag_get(USART1, USART_FLAG_RBNE)== SET) { //res[cpos] = (char) _get1_char(); cpos++; res[cpos] = (char) usart_data_receive(USART1); cpos++; }; }; res[cpos] = 0; } void setup() { wio_serial_init(); //Serial.begin(115200); //serial port of GD32 //Serial1.begin(115200); //serial port of ESP8266 pinMode(LED_BUILTIN, OUTPUT); delay(3000); usart0_print("\r\n\r\n\r\n\r\n\r\n"); // rest ESP8266 usart0_println("SEND:"); usart0_print("AT+RST\r\n"); usart1_print("AT+RST\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); // get firmware version usart0_println("SEND:"); usart0_print("AT+GMR\r\n"); usart1_print("AT+GMR\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_println("SEND:"); usart0_print("AT+CWQAP\r\n"); usart1_print("AT+CWQAP\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_println("SEND:"); usart0_print("AT+CWMODE=1\r\n"); usart1_print("AT+CWMODE=1\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); //Serial1.println("AT+CWJAP=\"Your WiFi SSID\",\"Password\""); // add your own wifi usart0_println("SEND:"); usart0_print("AT+CWJAP=\""); usart0_print(MY_SSID); usart0_print("\",\""); usart0_print(MY_PASSWD); usart0_print("\"\r\n"); usart1_print("AT+CWJAP=\""); usart1_print(MY_SSID); usart1_print("\",\""); usart1_print(MY_PASSWD); usart1_print("\"\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_println("SEND:"); usart0_print("AT+CIFSR\r\n"); usart1_print("AT+CIFSR\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); //-- get IP Address -- int len = strlen( res ); bool done=false; bool error = false; int pos = 0; while (!done) { if ( res[pos] == '\"') { done = true;} pos++; if (pos > len) { done = true; error = true;} } if (!error) { int buffpos = 0; done = false; while (!done) { if ( res[pos] == '\"' ) { done = true; } else { ipAddress[buffpos] = res[pos]; buffpos++; pos++;} } ipAddress[buffpos] = 0; } else { strcpy(ipAddress,"ERROR"); } //usart0_printf("ipAddress:%s\r\n",ipAddress); //----------------- usart0_println("SEND:"); usart0_print("AT+CIPMUX=1\r\n"); usart1_print("AT+CIPMUX=1\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_println("SEND:"); usart0_print("AT+CIPSERVER=1,80\r\n"); usart1_print("AT+CIPSERVER=1,80\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_printf("\r\nipAddress: %s\r\n\r\n",ipAddress); // UDP access // for UDP usart0_printf("AT+CIPSTART=1,\"UDP\",\"%s\",%d,%d,0\r\n",TARGET_IP,OUT_PORT,IN_PORT); usart1_printf("AT+CIPSTART=1,\"UDP\",\"%s\",%d,%d,0\r\n",TARGET_IP,OUT_PORT,IN_PORT); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); } //------------ void dispatch(void) { int datlen; int cp1,cp2,cp3; int len = (int)strlen(res); // keep length of res[] for (int i=0; i<len; i++) { if ((res[i]=='+') && (res[i+1]=='I') && (res[i+2]=='P') && (res[i+3]=='D') ) { // check +IPD // skip 1st ',' for (cp1 = i+4; cp1<len; cp1++) { if (res[cp1]==',') break; } // find 2nd ',' for (cp2 = cp1+1; cp2<len; cp2++) { if (res[cp2]==',') break; } char *dp = &res[cp2+1]; // find ':' for (cp3 = cp2+1; cp3<len; cp3++) { if (res[cp3]==':') break; } res[cp3]=0; // make string terminator datlen = atoi(dp); int d = 0; for (int s=cp3+1; s<(cp3+1+datlen); s++) { packet[d] = res[s]; d++; }; packet[d]=0; //usart0_printf("datalen: %d\r\n",datlen); // // decode OSC messeage decOSCmsg(packet, msg); usart0_printf("incomming OSC msg:\r\n%s %s\r\n", msg[0].address, msg[1].typeTag); for(int m=0; m < ((int)strlen(msg[1].typeTag)-1); m++) { if (msg[1].typeTag[m+1]=='f') usart0_printf(" %d/1000", (int)floor(1000*msg[m+2].f)); // %f not supported, replace with %d if (msg[1].typeTag[m+1]=='i') usart0_printf(" %d",msg[m+2].i); } usart0_printf("\r\n"); usart0_printf("Received packet from: %s \r\n", TARGET_IP); usart0_printf("-------------------\r\n"); } }; } //------------ void loop() { digitalWrite(LED_BUILTIN, HIGH); // debug: for indicate in-loop /** // test loop while(true) { if ( usart_flag_get(USART1, USART_FLAG_RBNE)== SET) _put0_char(_get1_char()); if ( usart_flag_get(USART0, USART_FLAG_RBNE)== SET) _put1_char(_get0_char()); }; **/ while(true) { get_res2(); if ((int)strlen(res) != 0) { //usart0_print("\r\nRES:\r\n"); //usart0_println(res); dispatch(); } else { //usart0_print("."); // indicating input wait } } }

以下については、自分の環境に合わせて変更すること:

#define MY_SSID "your_ssid" #define MY_PASSWD "your_passwd" #define TARGET_IP "192.168.0.23" #define OUT_PORT 9000 #define IN_PORT 8000

TARGET_IPは、対向するOSC送信デバイス(touchOSCなど)のIPを設定する。

書き込み後に「picocom /dev/ttyACM0 -b115200」で通信ソフトを起動すると以下のような出力が表示される:

$ picocom /dev/ttyACM0 -b115200 SEND: AT+GMR RES: AT+GMR AT version:2.1.0.0-dev(d25f6d2 - Oct 15 2019 12:03:04) SDK version:v3.2-192-g81655f39 compile time(525fbfe):Oct 17 2019 05:39:13 Bin version:2.0.0(WROOM-02) ... <省略> ... ----------- ipAddress: 192.168.0.26 # この時点でtouchOSCのfaderなどを動かす。 # (この例では、Wio-LiteのIPの"192.168.0.26"へOSCメッセージを送る) # 以下のように受信したOSCメッセージが表示される: incomming OSC msg: /1/fader2 ,f 983/1000 Received packet from: 192.168.0.23 ------------------- incomming OSC msg: /1/fader2 ,f 670/1000 Received packet from: 192.168.0.23 ------------------- incomming OSC msg: /1/fader2 ,f 554/1000 Received packet from: 192.168.0.23 ------------------- incomming OSC msg: /1/fader2 ,f 776/1000 Received packet from: 192.168.0.23 ------------------- incomming OSC msg: /1/fader2 ,f 700/1000 Received packet from: 192.168.0.23 ------------------- incomming OSC msg: /1/fader2 ,f 837/1000 Received packet from: 192.168.0.23 ------------------- incomming OSC msg: /1/fader2 ,f 575/1000 Received packet from: 192.168.0.23 ------------------- incomming OSC msg: /1/fader2 ,f 814/1000 Received packet from: 192.168.0.23 -------------------

printfの%fがサポートされていないので、整数の%dで代用している。(例:「814/1000」は、0.814を意味する)

OSC_send_test

OSC送信プログラム:
OSC受信プログラムのloop()関数を以下のものに置き換える:

void loop() { digitalWrite(LED_BUILTIN, HIGH); // debug: for indicate in-loop /** // test loop while(true) { if ( usart_flag_get(USART1, USART_FLAG_RBNE)== SET) _put0_char(_get1_char()); if ( usart_flag_get(USART0, USART_FLAG_RBNE)== SET) _put1_char(_get0_char()); }; **/ while(true){ // test send // fader // send OSC message with random values (to touchOSC) // make OSC message for sending outmsg[0].address="/1/fader5"; outmsg[1].typeTag=",f"; outmsg[2].f= rand()%1000/1000.0; memset(packet,0,sizeof(packet)); // clear send buff for OSC msg int plen=encOSCmsg(packet,outmsg); // send it ////usart0_printf("send: AT+CIPSEND=1,%d\r\n",plen); usart1_printf("AT+CIPSEND=1,%d\r\n",plen); delay(10); get_res2(); for(int i=0; i<plen; i++) _put1_char(packet[i]); delay(10); get_res2(); ////usart0_printf("res: %s\r\n",res); usart0_print("."); // indicating OSC sending } }

以下については、OSC受信でもプログラムと同様に自分の環境に合わせて変更すること:

#define MY_SSID "your_ssid" #define MY_PASSWD "your_passwd" #define TARGET_IP "192.168.0.14" #define OUT_PORT 9000 #define IN_PORT 8000

TARGET_IPは、対向するOSC受信デバイス(touchOSCなど)のIPを設定する。

書き込み後に「picocom /dev/ttyACM0 -b115200」で通信ソフトを起動すると以下のような出力が表示される:

$ picocom /dev/ttyACM0 -b115200 SEND: AT+RST RES: AT+RST OK I (1337827) wifi: state: 5 -> 0 (0) WIFI DISCONNECT rl ----------- SEND: AT+GMR RES: AT+GMR AT version:2.1.0.0-dev(d25f6d2 - Oct 15 2019 12:03:04) SDK version:v3.2-192-g81655f39 compile time(525fbfe):Oct 17 2019 05:39:13 Bin version:2.0.0(WROOM-02) OK ... <省略> ... ----------- ipAddress: 192.168.0.28 AT+CIPSTART=1,"UDP","192.168.0.14",9000,8000,0 RES: AT+CIPSTART=1,"UDP","192.168.0.14",9000,8000,0 local port:8000 change type:0 1,CONNECT OK ----------- #以下、OSC送信のたびに「.」が出力される。 .........................

動作としては、touchOSCのsimpleレイアウトの画面で、最上行の受信アイコンがOSCを受信すると「赤」になり、fader5のfaderが送られてきたOSCの内容に従ってランダムに変化する。

参考情報

TouchOSC - Modular touch control surface for OSC & MIDI
TouchOSC - iPhone
TouchOSC - Android

OSCmsgCode lib:
https://os.mbed.com/users/xshige/code/Sparkfun_CC3000_WiFi_OSCtranceiver//raw-file/e62251d890c1/OSCmsgCodec.h
https://os.mbed.com/users/xshige/code/Sparkfun_CC3000_WiFi_OSCtranceiver//raw-file/e62251d890c1/OSCmsgCodec.cpp
https://os.mbed.com/users/xshige/code/Sparkfun_CC3000_WiFi_OSCtranceiver//file/e62251d890c1/main.cpp/

Wio Lite RISC-V(GD32VF103)

Wio Lite RISC V GD32VF103 with ESP8266
Blink with a Wio Lite RISC-V with ESP8266

PlatformIO Core (CLI)

ESP8266をTCPサーバとTCPクライアントにするATコマンド実例 (1/4)
esp8266_at_command_examples_en.pdf

ESP8266をWifiモデムとして使う - ATコマンドによるMQTT通信
MQTT_via_ESP01
Arduino WiFi library for ESP8266 modules

espressif/ESP8266_AT - examples
Espressif Documentation

以上

続きを読む "Wio_Lite_RISC-VボードでWiFiを動かす(その3:OSC)"

| | コメント (0)

2020年6月21日 (日)

Wio_Lite_RISC-VボードでWiFiを動かす(その2:STARWARS,REST-API)

2020/6/22+:
初版

PlatformIO Wio Lite RISC-V WiFi2

PlatformIO Wio Lite RISC-V WiFi2

概要

Wio_Lite_RISC-VボードでWiFiを動かす(その2:STARWARS,REST-API)。 これは「Wio_Lite_RISC-VボードでWiFiを動かす」の続きになる。
開発ツール(PlatformIOなど)のインストールについては「開発ツールPlatformIOをcliで使う(Wio_Lite_RISC-V版)」を参照のこと、ここでは、WiFiを動かすためのプログラムについて記載する。 (ホストPCとしてはubuntuを想定している)

接続

wio-lite-rvボードのPA9(TX). PA10(RX)、GNDをUSBシリアルに接続する。
ちなみに、XIAOをUSBシリアルとして使用する場合は以下のように接続する:

wio-lite-rv XIAO
PA10(RX) D6(TX)
PA9(TX) D7(RX)
GND GND

starwars_test

telnetでASCIIアートの「StarWars」アニメを表示するプログラム:
src/starwars_test.cpp

#include <Arduino.h> #include <stdio.h> #include <stdarg.h> #define MY_SSID "your_ssid" #define MY_PASSWD "your_passwd" char ipAddress [20]; static void wio_serial_init(void) { // enable GPIO clock for USART0 rcu_periph_clock_enable(RCU_GPIOA); // enable USART clock rcu_periph_clock_enable(RCU_USART0); /* connect port to USARTx_Tx(PA9) */ gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); /* connect port to USARTx_Rx(PA10) */ gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10); // enable GPIO clock for USART1 rcu_periph_clock_enable(RCU_GPIOA); // enable USART clock rcu_periph_clock_enable(RCU_USART1); /* connect port to USARTx_Tx(PA2) */ gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2); /* connect port to USARTx_Rx(PA3) */ gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_3); // USART0 configure usart_deinit(USART0); usart_baudrate_set(USART0, 115200U); usart_word_length_set(USART0, USART_WL_8BIT); usart_stop_bit_set(USART0, USART_STB_1BIT); usart_parity_config(USART0, USART_PM_NONE); usart_hardware_flow_rts_config(USART0, USART_RTS_DISABLE); usart_hardware_flow_cts_config(USART0, USART_CTS_DISABLE); usart_receive_config(USART0, USART_RECEIVE_ENABLE); usart_transmit_config(USART0, USART_TRANSMIT_ENABLE); usart_enable(USART0); // USART1 configure usart_deinit(USART1); usart_baudrate_set(USART1, 115200U); usart_word_length_set(USART1, USART_WL_8BIT); usart_stop_bit_set(USART1, USART_STB_1BIT); usart_parity_config(USART1, USART_PM_NONE); usart_hardware_flow_rts_config(USART1, USART_RTS_DISABLE); usart_hardware_flow_cts_config(USART1, USART_CTS_DISABLE); usart_receive_config(USART1, USART_RECEIVE_ENABLE); usart_transmit_config(USART1, USART_TRANSMIT_ENABLE); usart_enable(USART1); } int _get0_char(void) { while ( usart_flag_get(USART0, USART_FLAG_RBNE)== RESET){ } return (uint8_t)usart_data_receive(USART0); } int _get1_char(void) { while ( usart_flag_get(USART1, USART_FLAG_RBNE)== RESET){ } return (uint8_t)usart_data_receive(USART1); } int _put0_char(int ch) { usart_data_transmit(USART0, (uint8_t) ch ); while ( usart_flag_get(USART0, USART_FLAG_TBE)== RESET){ } return ch; } int _put1_char(int ch) { usart_data_transmit(USART1, (uint8_t) ch ); while ( usart_flag_get(USART1, USART_FLAG_TBE)== RESET){ } return ch; } void usart0_printf(const char *fmt, ...) { char buf[100]; va_list args; va_start(args, fmt); vsprintf(buf, fmt, args); va_end(args); char *p = buf; while( *p != '\0' ) { _put0_char(*p); p++; } } void usart0_print(const char *s) { while( *s != '\0' ) { _put0_char(*s); s++; } } void usart0_println(const char *s) { while( *s != '\0' ) { _put0_char(*s); s++; } _put0_char('\r'); _put0_char('\n'); } void usart1_printf(const char *fmt, ...) { char buf[100]; va_list args; va_start(args, fmt); vsprintf(buf, fmt, args); va_end(args); char *p = buf; while( *p != '\0' ) { _put1_char(*p); p++; } } void usart1_print(const char *s) { while( *s != '\0' ) { _put1_char(*s); s++; } } void usart1_println(const char *s) { while( *s != '\0' ) { _put1_char(*s); s++; } _put1_char('\r'); _put1_char('\n'); } int usart1_available(void) { return ( usart_flag_get(USART1, USART_FLAG_RBNE)== SET); } //------------------------------------------------------- //char res[500]; char res[3000]; // need big buffer for StarWars void get_res(void) { int cpos = 0; for(int cc=0; cc<24000000; cc++) { while ( usart_flag_get(USART1, USART_FLAG_RBNE)== SET) { //res[cpos] = (char) _get1_char(); cpos++; res[cpos] = (char) usart_data_receive(USART1); cpos++; }; }; res[cpos] = 0; } void get_res2(void) { int cpos = 0; for(int cc=0; cc<500000; cc++) { while ( usart_flag_get(USART1, USART_FLAG_RBNE)== SET) { //res[cpos] = (char) _get1_char(); cpos++; res[cpos] = (char) usart_data_receive(USART1); cpos++; }; }; res[cpos] = 0; } void setup() { wio_serial_init(); //Serial.begin(115200); //serial port of GD32 //Serial1.begin(115200); //serial port of ESP8266 pinMode(LED_BUILTIN, OUTPUT); delay(3000); usart0_print("\r\n\r\n\r\n\r\n\r\n"); // get firmware version usart0_println("SEND:"); usart0_print("AT+GMR\r\n"); usart1_print("AT+GMR\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_println("SEND:"); usart0_print("AT+CWQAP\r\n"); usart1_print("AT+CWQAP\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_println("SEND:"); usart0_print("AT+CWMODE=1\r\n"); usart1_print("AT+CWMODE=1\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); //Serial1.println("AT+CWJAP=\"Your WiFi SSID\",\"Password\""); // add your own wifi usart0_println("SEND:"); usart0_print("AT+CWJAP=\""); usart0_print(MY_SSID); usart0_print("\",\""); usart0_print(MY_PASSWD); usart0_print("\"\r\n"); usart1_print("AT+CWJAP=\""); usart1_print(MY_SSID); usart1_print("\",\""); usart1_print(MY_PASSWD); usart1_print("\"\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_println("SEND:"); usart0_print("AT+CIFSR\r\n"); usart1_print("AT+CIFSR\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); //-- get IP Address -- int len = strlen( res ); bool done=false; bool error = false; int pos = 0; while (!done) { if ( res[pos] == '\"') { done = true;} pos++; if (pos > len) { done = true; error = true;} } if (!error) { int buffpos = 0; done = false; while (!done) { if ( res[pos] == '\"' ) { done = true; } else { ipAddress[buffpos] = res[pos]; buffpos++; pos++;} } ipAddress[buffpos] = 0; } else { strcpy(ipAddress,"ERROR"); } //usart0_printf("ipAddress:%s\r\n",ipAddress); //----------------- usart0_println("SEND:"); usart0_print("AT+CIPMUX=1\r\n"); usart1_print("AT+CIPMUX=1\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_println("SEND:"); usart0_print("AT+CIPSERVER=1,80\r\n"); usart1_print("AT+CIPSERVER=1,80\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_printf("\r\nipAddress: %s\r\n\r\n",ipAddress); // telnet access usart0_print("AT+CIPSTART=1,\"TCP\",\"towel.blinkenlights.nl\",23\r\n"); usart1_print("AT+CIPSTART=1,\"TCP\",\"towel.blinkenlights.nl\",23\r\n"); /*** get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); ***/ /**** usart0_print("AT+CIPSTART=\"TCP\",\"imaoca.webcrow.jp\",80\r\n"); usart1_print("AT+CIPSTART=\"TCP\",\"imaoca.webcrow.jp\",80\r\n"); ****/ } //------------ void dispatch(void) { // setup string pointer table char *h[20]; for(int i=0; i<20; i++) h[i]=NULL; h[0] = &res[0]; int pi = 1; int cp; int len = (int)strlen(res); // keep length of res[] for (int i=0; i<len; i++) { if ((res[i]=='+') && (res[i+1]=='I') && (res[i+2]=='P') && (res[i+3]=='D') ) { // check +IPD for (cp = i+4; cp<len; cp++) { if (res[cp]==':') break; } res[i] = 0; // make string terminator h[pi] = &res[cp+1]; pi++; } }; // display removed IPD header for(int i=0; i<20; i++) { if (h[i] != NULL) usart0_print(h[i]); } } //------------ void loop() { digitalWrite(LED_BUILTIN, HIGH); // debug: for indicate in-loop /** // test loop while(true) { if ( usart_flag_get(USART1, USART_FLAG_RBNE)== SET) _put0_char(_get1_char()); if ( usart_flag_get(USART0, USART_FLAG_RBNE)== SET) _put1_char(_get0_char()); }; **/ while(true) { get_res2(); if ((int)strlen(res) != 0) { //usart0_print("\r\nRES:\r\n"); //usart0_println(res); dispatch(); } else { //usart0_print("."); // indicating input wait } } }

以下については、自分の環境に合わせて変更すること:
#define MY_SSID "your_ssid"
#define MY_PASSWD "your_passwd"

書き込み後に「picocom /dev/ttyACM0 -b115200」で通信ソフトを起動すると以下のような出力が表示される:

$ picocom /dev/ttyACM0 -b115200 SEND: AT+GMR RES: AT+GMR AT version:2.1.0.0-dev(d25f6d2 - Oct 15 2019 12:03:04) SDK version:v3.2-192-g81655f39 compile time(525fbfe):Oct 17 2019 05:39:13 Bin version:2.0.0(WROOM-02) ... <省略> ... ----------- ipAddress: 192.168.0.14 # StarWarsのASCIIアートのアニメが表示される

実行するとStarWarsのASCIIアートのアニメが表示される。

restapi_test

RESI-APIでボードを制御するサーバープログラム。
src/restapi_test.cpp

#include <Arduino.h> #include <stdio.h> #include <string.h> #include <stdarg.h> #define MY_SSID "your_ssid" #define MY_PASSWD "your_passwd" char ipAddress [20]; static void wio_serial_init(void) { // enable GPIO clock for USART0 rcu_periph_clock_enable(RCU_GPIOA); // enable USART clock rcu_periph_clock_enable(RCU_USART0); /* connect port to USARTx_Tx(PA9) */ gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); /* connect port to USARTx_Rx(PA10) */ gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10); // enable GPIO clock for USART1 rcu_periph_clock_enable(RCU_GPIOA); // enable USART clock rcu_periph_clock_enable(RCU_USART1); /* connect port to USARTx_Tx(PA2) */ gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2); /* connect port to USARTx_Rx(PA3) */ gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_3); // USART0 configure usart_deinit(USART0); usart_baudrate_set(USART0, 115200U); usart_word_length_set(USART0, USART_WL_8BIT); usart_stop_bit_set(USART0, USART_STB_1BIT); usart_parity_config(USART0, USART_PM_NONE); usart_hardware_flow_rts_config(USART0, USART_RTS_DISABLE); usart_hardware_flow_cts_config(USART0, USART_CTS_DISABLE); usart_receive_config(USART0, USART_RECEIVE_ENABLE); usart_transmit_config(USART0, USART_TRANSMIT_ENABLE); usart_enable(USART0); // USART1 configure usart_deinit(USART1); usart_baudrate_set(USART1, 115200U); usart_word_length_set(USART1, USART_WL_8BIT); usart_stop_bit_set(USART1, USART_STB_1BIT); usart_parity_config(USART1, USART_PM_NONE); usart_hardware_flow_rts_config(USART1, USART_RTS_DISABLE); usart_hardware_flow_cts_config(USART1, USART_CTS_DISABLE); usart_receive_config(USART1, USART_RECEIVE_ENABLE); usart_transmit_config(USART1, USART_TRANSMIT_ENABLE); usart_enable(USART1); } int _get0_char(void) { while ( usart_flag_get(USART0, USART_FLAG_RBNE)== RESET){ } return (uint8_t)usart_data_receive(USART0); } int _get1_char(void) { while ( usart_flag_get(USART1, USART_FLAG_RBNE)== RESET){ } return (uint8_t)usart_data_receive(USART1); } int _put0_char(int ch) { usart_data_transmit(USART0, (uint8_t) ch ); while ( usart_flag_get(USART0, USART_FLAG_TBE)== RESET){ } return ch; } int _put1_char(int ch) { usart_data_transmit(USART1, (uint8_t) ch ); while ( usart_flag_get(USART1, USART_FLAG_TBE)== RESET){ } return ch; } void usart0_printf(const char *fmt, ...) { char buf[100]; va_list args; va_start(args, fmt); vsprintf(buf, fmt, args); va_end(args); char *p = buf; while( *p != '\0' ) { _put0_char(*p); p++; } } void usart0_print(const char *s) { while( *s != '\0' ) { _put0_char(*s); s++; } } void usart0_println(const char *s) { while( *s != '\0' ) { _put0_char(*s); s++; } _put0_char('\r'); _put0_char('\n'); } void usart1_printf(const char *fmt, ...) { char buf[100]; va_list args; va_start(args, fmt); vsprintf(buf, fmt, args); va_end(args); char *p = buf; while( *p != '\0' ) { _put1_char(*p); p++; } } void usart1_print(const char *s) { while( *s != '\0' ) { _put1_char(*s); s++; } } void usart1_println(const char *s) { while( *s != '\0' ) { _put1_char(*s); s++; } _put1_char('\r'); _put1_char('\n'); } int usart1_available(void) { return ( usart_flag_get(USART1, USART_FLAG_RBNE)== SET); } //------------------------------------------------------- char html[50]; char command[20]; //char LED[30]; int lenHtml = 0; char temp[5]; char res[500]; void get_res(void) { int cpos = 0; for(int cc=0; cc<24000000; cc++) { while ( usart_flag_get(USART1, USART_FLAG_RBNE)== SET) { //res[cpos] = (char) _get1_char(); cpos++; res[cpos] = (char) usart_data_receive(USART1); cpos++; }; }; res[cpos] = 0; } void get_resCmd(void) { int cpos = 0; for(int cc=0; cc<100000; cc++) { while ( usart_flag_get(USART1, USART_FLAG_RBNE)== SET) { //res[cpos] = (char) _get1_char(); cpos++; res[cpos] = (char) usart_data_receive(USART1); cpos++; }; }; res[cpos] = 0; } void get_resHtml(void) { int cpos = 0; for(int cc=0; cc<1000000; cc++) { while ( usart_flag_get(USART1, USART_FLAG_RBNE)== SET) { //res[cpos] = (char) _get1_char(); cpos++; res[cpos] = (char) usart_data_receive(USART1); cpos++; }; }; res[cpos] = 0; } void setup() { wio_serial_init(); //Serial.begin(115200); //serial port of GD32 //Serial1.begin(115200); //serial port of ESP8266 pinMode(LED_BUILTIN, OUTPUT); delay(3000); usart0_print("\r\n\r\n\r\n\r\n\r\n"); // get firmware version usart0_println("SEND:"); usart0_print("AT+GMR\r\n"); usart1_print("AT+GMR\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_println("SEND:"); usart0_print("AT+CWQAP\r\n"); usart1_print("AT+CWQAP\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_println("SEND:"); usart0_print("AT+CWMODE=1\r\n"); usart1_print("AT+CWMODE=1\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); //Serial1.println("AT+CWJAP=\"Your WiFi SSID\",\"Password\""); // add your own wifi usart0_println("SEND:"); usart0_print("AT+CWJAP=\""); usart0_print(MY_SSID); usart0_print("\",\""); usart0_print(MY_PASSWD); usart0_print("\"\r\n"); usart1_print("AT+CWJAP=\""); usart1_print(MY_SSID); usart1_print("\",\""); usart1_print(MY_PASSWD); usart1_print("\"\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_println("SEND:"); usart0_print("AT+CIFSR\r\n"); usart1_print("AT+CIFSR\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); //-- get IP Address -- int len = strlen( res ); bool done=false; bool error = false; int pos = 0; while (!done) { if ( res[pos] == '\"') { done = true;} pos++; if (pos > len) { done = true; error = true;} } if (!error) { int buffpos = 0; done = false; while (!done) { if ( res[pos] == '\"' ) { done = true; } else { ipAddress[buffpos] = res[pos]; buffpos++; pos++;} } ipAddress[buffpos] = 0; } else { strcpy(ipAddress,"ERROR"); } //usart0_printf("ipAddress:%s\r\n",ipAddress); //----------------- usart0_println("SEND:"); usart0_print("AT+CIPMUX=1\r\n"); usart1_print("AT+CIPMUX=1\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_println("SEND:"); usart0_print("AT+CIPSERVER=1,80\r\n"); usart1_print("AT+CIPSERVER=1,80\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_printf("\r\nipAddress: %s\r\n\r\n",ipAddress); } //------------ int count = 0; // test variable void dispatch(void) { bool foundIPD = false; for (int i=0; i<(int)strlen(res); i++) { if ( (res[i]=='I') && (res[i+1]=='P') && (res[i+2]=='D')) { foundIPD = true;} } bool foundGET = false; int pGET; if (foundIPD) { for (int i=0; i<(int)strlen(res); i++) { // check 'GET' if ((res[i]=='G') && (res[i+1]=='E') && (res[i+2]=='T')) { foundGET = true; pGET = i+3; break; } } } bool foundApi = false; char api[24]; for(int i=0; i<24; i++) api[i]=0; int dp = 0; if (foundGET) { for (int i=pGET; i<(int)strlen(res); i++) { // check '/api/v1/' if ((res[i]=='/') && (res[i+1]=='a') && (res[i+2]=='p') && (res[i+3]=='i') && (res[i+4]=='/') && (res[i+5]=='v') && (res[i+6]=='1') && (res[i+7]=='/') ) { foundApi = true; int sp = i+8; while (res[sp]!=' ') { api[dp]= res[sp]; dp++; sp++; } api[dp]=0; } // if check '/api/v1/' } } if (foundApi) { usart0_printf("API: %s\r\n", api); // if(strcmp(api,"led=0")==0){ usart0_print("LED:off\r\n"); digitalWrite(LED_BUILTIN, LOW); // sprintf(html,"{ \"LED\" : \"off\" }\r\n"); sprintf(command,"AT+CIPSEND=0,%d\r\n",(int)strlen(html)); usart0_print("DEBUG-CMD:\r\n"); usart0_print(command); usart0_print("\r\n-------------\r\n"); usart0_print("DEBUG-HTML:\r\n"); usart0_print(html); usart0_print("\r\n=============\r\n\r\n"); usart1_print(command); get_resCmd(); usart1_print(html); get_resHtml(); } if(strcmp(api,"led=1")==0){ usart0_print("LED:on\r\n"); digitalWrite(LED_BUILTIN, HIGH); // sprintf(html,"{ \"LED\" : \"on\" }\r\n"); sprintf(command,"AT+CIPSEND=0,%d\r\n",(int)strlen(html)); usart0_print("DEBUG-CMD:\r\n"); usart0_print(command); usart0_print("\r\n-------------\r\n"); usart0_print("DEBUG-HTML:\r\n"); usart0_print(html); usart0_print("\r\n=============\r\n\r\n"); usart1_print(command); get_resCmd(); usart1_print(html); get_resHtml(); } if(strcmp(api,"count")==0){ usart0_printf("count:%d\r\n",count); // sprintf(html,"{ \"count\" : %d}\r\n", count); sprintf(command,"AT+CIPSEND=0,%d\r\n",(int)strlen(html)); usart0_print("DEBUG-CMD:\r\n"); usart0_print(command); usart0_print("\r\n-------------\r\n"); usart0_print("DEBUG-HTML:\r\n"); usart0_print(html); usart0_print("\r\n=============\r\n\r\n"); usart1_print(command); get_resCmd(); usart1_print(html); get_resHtml(); } } // if (foundApi) // close connection (is needed for displaying JSON data on the web) strcpy(command,"AT+CIPCLOSE=0\r\n"); // usart0_print("DEBUG-CMD:\r\n"); usart0_print(command); usart0_print("\r\n=============\r\n\r\n"); // usart1_print(command); get_resCmd(); //----- count++; // update test variable return; } void loop() { /*** // test loop while(true) { if ( usart_flag_get(USART1, USART_FLAG_RBNE)== SET) _put0_char(_get1_char()); if ( usart_flag_get(USART0, USART_FLAG_RBNE)== SET) _put1_char(_get0_char()); }; ****/ while(true) { get_resHtml(); if ((int)strlen(res) != 0) { usart0_print("\r\nRES:\r\n"); usart0_println(res); dispatch(); } else { usart0_print("."); // indicating input wait } } }

以下については、自分の環境に合わせて変更すること:
#define MY_SSID "your_ssid"
#define MY_PASSWD "your_passwd"

「picocom /dev/ttyACM0 -b115200」を起動すると以下が表示される:

$ picocom /dev/ttyACM0 -b115200 SEND: AT+GMR RES: lAT+GMR AT version:2.1.0.0-dev(d25f6d2 - Oct 15 2019 12:03:04) SDK version:v3.2-192-g81655f39 compile time(525fbfe):Oct 17 2019 05:39:13 Bin version:2.0.0(WROOM-02) ... <省略> ... ----------- ipAddress: 192.168.0.14 ....................................................................... RES: 0,CONNECT +IPD,0,88:GET /api/v1/led=1 HTTP/1.1 Host: 192.168.0.14 User-Agent: curl/7.60.0 Accept: */* API: led=1 LED:on DEBUG-CMD: AT+CIPSEND=0,19 ------------- DEBUG-HTML: { "LED" : "on" } ============= DEBUG-CMD: AT+CIPCLOSE=0 ============= ... ...

起動してIPが確定した後、REST-APIをサポートしているので以下のようなコマンドでボードを制御できる:

$ curl 'http://192.168.0.14:80/api/v1/led=1' { "LED" : "on" } #ここの時点でオンボードのLEDが光る $ curl 'http://192.168.0.14:80/api/v1/led=0' { "LED" : "off" } #ここの時点でオンボードのLEDが消える $ curl 'http://192.168.0.14:80/api/v1/led=1' { "LED" : "on" } #ここの時点でオンボードのLEDが光る $ curl 'http://192.168.0.14:80/api/v1/count' { "count" : 18} #現在の変数countの内容が表示される $ curl 'http://192.168.0.14:80/api/v1/count' { "count" : 20} #現在の変数countの内容が表示される $ curl 'http://192.168.0.14:80/api/v1/count' { "count" : 22} #現在の変数countの内容が表示される $ curl 'http://192.168.0.14:80/api/v1/count' { "count" : 24} #現在の変数countの内容が表示される

上で使用していると同じurlをウェブブラウザーに入力してアクセスしても 同様に制御できる。 IPが「192.168.0.14」の場合なので、実際に使うときは割り当てられたIPでアクセスすること。

参考情報

Wio Lite RISC-V(GD32VF103)

Wio Lite RISC V GD32VF103 with ESP8266
Blink with a Wio Lite RISC-V with ESP8266

PlatformIO Core (CLI)

ESP8266をTCPサーバとTCPクライアントにするATコマンド実例 (1/4)
esp8266_at_command_examples_en.pdf

ESP8266をWifiモデムとして使う - ATコマンドによるMQTT通信
MQTT_via_ESP01
Arduino WiFi library for ESP8266 modules

espressif/ESP8266_AT - examples
Espressif Documentation

以上

続きを読む "Wio_Lite_RISC-VボードでWiFiを動かす(その2:STARWARS,REST-API)"

| | コメント (0)

2020年5月19日 (火)

Longan-NanoでLCDとSerialを使う(framework:Arduino版)

2020/5/19

PlatformIO Longan Nano LCD Serial

PlatformIO Longan Nano LCD Serial

概要

Longan-NanoでLCDとSerialを使う(framework:Arduino版)。 (ホストPCとしてはubuntuを想定している)

PlatformIOのインストール

python3 -m venv pio_env source pio_env/bin/activate pip3 install platformio インストール後も、本ツールを使用する場合 同じディレクトリで以下を実行する: source pio_env/bin/activate # 「source」は、「.」でも良い

準備

以下を実行して、udevのrulesを登録する:

curl -fsSL https://raw.githubusercontent.com/platformio/platformio-core/master/scripts/99-platformio-udev.rules | sudo tee /etc/udev/rules.d/99-platformio-udev.rules sudo udevadm control --reload-rules sudo usermod -a -G dialout $USER sudo usermod -a -G plugdev $USER

テスト用プロジェクト arduino-lcd-serial を作成/実行する

#ターゲットボードのtarget名を検索する # (ここではlonganを検索する) pio boards | grep -i logan #出力例: sipeed-longan-nano GD32VF103CBT6 108MHz 128KB 32KB Sipeed Longan Nano sipeed-longan-nano-lite GD32VF103C8T6 108MHz 64KB 20KB Sipeed Longan Nano Lite # flash容量が2種類あるが、持っているボードのものを選択する # 128kBのほうを持っているので、target名としては「sipeed-longan-nano」になる。 # プロジェクト arduino-lcd-serial のディレクトリを作成する #(サンプルmicrosを流用する) git clone https://github.com/smuehlst/longan-nano-experiments.git cd longan-nano-experiments cp -r micros arduino-lcd-serial cd arduino-lcd-serial nano platformio.ini # 以下の内容になるように編集する:
; PlatformIO Project Configuration File ; ; Build options: build flags, source filter, extra scripting ; Upload options: custom port, speed and extra flags ; Library options: dependencies, extra library storages ; ; Please visit documentation for the other options and examples ; http://docs.platformio.org/page/projectconf.html [env:sipeed-longan-nano] platform = gd32v framework = arduino board = sipeed-longan-nano monitor_speed = 115200 platform_packages = framework-arduino-gd32v @ https://github.com/sipeed/Longduino.git upload_protocol = dfu debug_tool = sipeed-rv-debugger #upload_protocol = um232h #debug_tool = um232h #upload_protocol = sipeed-rv-debugger #debug_tool = sipeed-rv-debugger

「upload_protocol = 」、「debug_tool = 」は、自分の環境に合わせること。

続き:

# テスト用のmain.cを作成する nano src/main.cpp 次の内容に編集(差し替え)する:
#include <Arduino.h> extern "C" { #include "lcd/lcd.h" } #include "gd32vf103.h" #include <stdio.h> #include <inttypes.h> static void longan_oled_init(void) { Lcd_Init(); LCD_Clear(BLACK); BACK_COLOR = BLACK; } static void serial_init(void) { /* enable GPIO clock */ rcu_periph_clock_enable(RCU_GPIOA); /* enable USART clock */ rcu_periph_clock_enable(RCU_USART0); /* connect port to USARTx_Tx */ gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); /* connect port to USARTx_Rx */ gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10); /* USART configure */ usart_deinit(USART0); usart_baudrate_set(USART0, 115200U); usart_word_length_set(USART0, USART_WL_8BIT); usart_stop_bit_set(USART0, USART_STB_1BIT); usart_parity_config(USART0, USART_PM_NONE); usart_hardware_flow_rts_config(USART0, USART_RTS_DISABLE); usart_hardware_flow_cts_config(USART0, USART_CTS_DISABLE); usart_receive_config(USART0, USART_RECEIVE_ENABLE); usart_transmit_config(USART0, USART_TRANSMIT_ENABLE); usart_enable(USART0); } /* retarget the C library printf function to the USART */ int _put_char(int ch) { usart_data_transmit(USART0, (uint8_t) ch ); while ( usart_flag_get(USART0, USART_FLAG_TBE)== RESET){ } return ch; } //=========================================== int a0; float a1; int a2; int a3; int a4; int a5; void setup() { longan_oled_init(); serial_init(); // initialize LED digital pin as an output. pinMode(LED_BUILTIN, OUTPUT); // LED_R pinMode(PA1, OUTPUT); // LED_G pinMode(PA2, OUTPUT); // LED_B a0 = 1234; a1 = 45.78; a2 = 910; a3 = 2345; a4 = 6789; printf("serial start...\n\r"); } void loop() { char s[256] = {'\0'}; // buffer for sprintf // display lcd sprintf(s, "A0: %d ", a0); LCD_ShowString(0, 0, (u8 const *)s, WHITE); int f0 = (int)a1; int f1 = (int)((a1-f0)*100); sprintf(s, "A1: %d.%02d ", f0,f1); LCD_ShowString(0, 16, (u8 const *)s, GREEN); sprintf(s, "A2: %d ", a2); LCD_ShowString(0, 32, (u8 const *)s, BRED); sprintf(s, "A3: %d ", a3); LCD_ShowString(0, 48, (u8 const *)s, GBLUE); sprintf(s, "A4: %d ", a4); LCD_ShowString(0, 64, (u8 const *)s, RED); // value print serial printf(" A0: %d\r\n", a0); printf(" A1: %d.%02d\r\n", f0, f1); printf(" A2: %d\r\n", a2); printf(" A3: %d\r\n", a3); printf(" A4: %d\r\n", a4); printf("-------------- \r\n\r\n"); a0 += 1; a1 += 0.01; a2 += 1; a3 += 1; a4 += 1; // LED Red digitalWrite(LED_BUILTIN, LOW); digitalWrite(PA1, HIGH); digitalWrite(PA2, HIGH); delay(200); // LED Green digitalWrite(LED_BUILTIN, HIGH); digitalWrite(PA1, LOW); digitalWrite(PA2, HIGH); delay(200); // LED Blue digitalWrite(LED_BUILTIN, HIGH); digitalWrite(PA1, HIGH); digitalWrite(PA2, LOW); delay(200); }

続き:

# build pio run # ボードをPCに接続して # 以下の手順で書き込みモード(DFUモード)にする: # (1)「BOOT」ボタンと「RESET」ボタンも同時に押す。 # (2)1秒くらいして「RESET」ボタンを離す。 # (3)その後、「BOOT」も離す。 # build&upload(flash) pio run -t upload # buildしないで書き込む場合は以下を実行する: pio run -t nobuild -t upload -v # -v は、詳細を表示するオプション # 以上で、基本的な操作としては完了となる

書き込み後、LCDに変数の値(a0,a1,a2,a3,a4)が表示される。 シリアル出力に変数の値(a0,a1,a2,a3,a4)が出力される。
LEDは、赤、緑、青の順で繰り返し色が変化する。

補足

TODO list #1を見るとanalogReadの実装は終わっているようだが、実際にanalogRead(0)などを動かしてみると-1を値として返すようだ。(なにかしらのupdateが上手く行っていない??)

以下に確かにソースは存在している。。。
https://github.com/sipeed/Longduino/blob/master/cores/arduino/wiring_analog.c

参考情報

Longan Nano(Schematic).pdf
GD32VF103_Firmware_Library_User_Guide_V1.0.pdf

Sipeed Longan Nano RISC-V GD32VF103CBT6開発ボード
■主な仕様
・CPU:GD32VF103CBT6
・メモリ:128KB Flash/32KB SRAM
・160×80ドット、0.96インチのフルカラーIPS液晶
・TFスロット(microSDスロット)

Longan Nano PINOUT
download Longan Nano Datasheet

PlatformIO Core (CLI)

Longan nano で Hello World!
Sipeed Longan Nanoで文字を表示してみる
・図形描画の関数の説明がある
Sipeed Longan Nano 用の、FONTX2対応LCD描画ライブラリとサンプルプログラム

以上

続きを読む "Longan-NanoでLCDとSerialを使う(framework:Arduino版)"

| | コメント (0)

2020年5月18日 (月)

Longan-NanoでADCを使う(framework:gd32v版)

2020/5/18+

PlatformIO Longan Nano ADC

PlatformIO Longan Nano ADC

概要

Longan-NanoでADC使う(framework:gd32v版)。 (ホストPCとしてはubuntuを想定している)

PlatformIOのインストール

python3 -m venv pio_env source pio_env/bin/activate pip3 install platformio インストール後も、本ツールを使用する場合 同じディレクトリで以下を実行する: source pio_env/bin/activate # 「source」は、「.」でも良い

準備

以下を実行して、udevのrulesを登録する:

curl -fsSL https://raw.githubusercontent.com/platformio/platformio-core/master/scripts/99-platformio-udev.rules | sudo tee /etc/udev/rules.d/99-platformio-udev.rules sudo udevadm control --reload-rules sudo usermod -a -G dialout $USER sudo usermod -a -G plugdev $USER

テスト用プロジェクト sample-adc を作成/実行する

#ターゲットボードのtarget名を検索する # (ここではlonganを検索する) pio boards | grep -i logan #出力例: sipeed-longan-nano GD32VF103CBT6 108MHz 128KB 32KB Sipeed Longan Nano sipeed-longan-nano-lite GD32VF103C8T6 108MHz 64KB 20KB Sipeed Longan Nano Lite # flash容量が2種類あるが、持っているボードのものを選択する # 128kBのほうを持っているので、target名としては「sipeed-longan-nano」になる。 # プロジェクト sample-adc のディレクトリを作成する #(サンプルgd32v_lcdを流用する) git clone https://github.com/sipeed/Longan_GD32VF_examples.git cd Longan_GD32VF_examples cp -r gd32v_lcd sample-adc cd sample-adc nano platformio.ini # 以下の内容になるように編集する:
;PlatformIO Project Configuration File ; ; Build options: build flags, source filter ; Upload options: custom upload port, speed and extra flags ; Library options: dependencies, extra library storages ; Advanced options: extra scripting ; ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html [env:sipeed-longan-nano] platform = gd32v board = sipeed-longan-nano framework = gd32vf103-sdk upload_protocol = dfu debug_tool = sipeed-rv-debugger #upload_protocol = um232h #debug_tool = um232h #upload_protocol = sipeed-rv-debugger #debug_tool = sipeed-rv-debugger

「upload_protocol = 」、「debug_tool = 」は、自分の環境に合わせること。

続き:

# テスト用のmain.cを作成する nano src/main.c 次の内容に編集(差し替え)する:
#include "gd32vf103.h" #include "systick.h" #include <stdio.h> //#include "gd32vf103v_eval.h" #include "lcd/lcd.h" #include "fatfs/tf_card.h" #include <string.h> uint16_t a0; uint16_t a1; uint16_t a2; uint16_t a3; uint16_t a4; uint16_t a5; uint16_t a6; uint16_t a7; uint16_t b0; uint16_t b1; void init_uart0(void) { /* enable GPIO clock */ rcu_periph_clock_enable(RCU_GPIOA); /* enable USART clock */ rcu_periph_clock_enable(RCU_USART0); /* connect port to USARTx_Tx */ gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); /* connect port to USARTx_Rx */ gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10); /* USART configure */ usart_deinit(USART0); usart_baudrate_set(USART0, 115200U); usart_word_length_set(USART0, USART_WL_8BIT); usart_stop_bit_set(USART0, USART_STB_1BIT); usart_parity_config(USART0, USART_PM_NONE); usart_hardware_flow_rts_config(USART0, USART_RTS_DISABLE); usart_hardware_flow_cts_config(USART0, USART_CTS_DISABLE); usart_receive_config(USART0, USART_RECEIVE_ENABLE); usart_transmit_config(USART0, USART_TRANSMIT_ENABLE); usart_enable(USART0); usart_interrupt_enable(USART0, USART_INT_RBNE); } int _put_char(int ch) { usart_data_transmit(USART0, (uint8_t) ch ); while ( usart_flag_get(USART0, USART_FLAG_TBE)== RESET){ } return ch; } void adc_init(void) { rcu_periph_clock_enable(RCU_GPIOA); gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, GPIO_PIN_0); RCU_CFG0 |= (0b10 << 14) | (1 << 28); // ADC clock = 108MHz / 8 = 13.5MHz(14MHz max.) rcu_periph_clock_enable(RCU_ADC0); ADC_CTL1(ADC0) |= ADC_CTL1_ADCON; } uint16_t get_adc(int ch) { ADC_RSQ2(ADC0) = 0; ADC_RSQ2(ADC0) = ch; ADC_CTL1(ADC0) |= ADC_CTL1_ADCON; while( !(ADC_STAT(ADC0) & ADC_STAT_EOC) ); uint16_t ret = ADC_RDATA(ADC0) & 0xFFFF; ADC_STAT(ADC0) &= ~ADC_STAT_EOC; return ret; } int main(void) { unsigned char s[256] = {'\0'}; /* system clocks configuration */ // rcu_config(); /* ADC configuration */ adc_init(); init_uart0(); Lcd_Init(); // init OLED LCD_Clear(BLACK); BACK_COLOR=BLACK; while(1){ a0 = get_adc(0); a1 = get_adc(1); a2 = get_adc(2); a3 = get_adc(3); a4 = get_adc(4); a5 = get_adc(5); a6 = get_adc(6); a7 = get_adc(7); b0 = get_adc(8); b1 = get_adc(9); // value print printf(" A0: %d\r\n", a0); printf(" A1: %d\r\n", a1); printf(" A2: %d\r\n", a2); printf(" A3: %d\r\n", a3); printf(" A4: %d\r\n", a4); printf(" A5: %d\r\n", a5); printf(" A6: %d\r\n", a6); printf(" A7: %d\r\n", a7); printf(" B0: %d\r\n", b0); printf(" B1: %d\r\n", b1); printf("-------------- \r\n\r\n"); sprintf(s, "A0: %d ", a0); LCD_ShowString(0, 0, &s, WHITE); float v0 = a0 * 3.3 / 4095.0; int f0 = (int)v0; int f1 = (int)((v0-f0)*100); sprintf(s, "A0(V): %d.%02d ", f0,f1); LCD_ShowString(0, 16, &s, GREEN); sprintf(s, "A3: %d ", a3); LCD_ShowString(0, 32, &s, BRED); sprintf(s, "A4: %d ", a4); LCD_ShowString(0, 48, &s, GBLUE); sprintf(s, "A6: %d ", a6); LCD_ShowString(0, 64, &s, RED); } }

続き:

# build pio run # ボードをPCに接続して # 以下の手順で書き込みモード(DFUモード)にする: # (1)「BOOT」ボタンと「RESET」ボタンも同時に押す。 # (2)1秒くらいして「RESET」ボタンを離す。 # (3)その後、「BOOT」も離す。 # build&upload(flash) pio run -t upload # buildしないで書き込む場合は以下を実行する: pio run -t nobuild -t upload -v # -v は、詳細を表示するオプション # 以上で、基本的な操作としては完了となる

書き込み後、LCDにADCの値(PA0,PA3,PA4,PA6)が表示される。 シリアル出力にもADCの値(PA0からPA7,PB0,PB1)が出力される。
ただし、以下のポートは、他の機能端子と兼用になっているので 独立して使用できるのは、A0,PA3,PA4,PA6になる。

port func
PA1 LED_G
PA2 LED_B
PA5 TFT_SCL
PA7 TFT_SDA
PB0 TFT_RS
PB1 TFT_RST

参考情報

Longan NanoでRISC-Vチャレンジ(7) - ADC/DAC
Longan Nano(Schematic).pdf
GD32VF103_Firmware_Library_User_Guide_V1.0.pdf

Sipeed Longan Nano RISC-V GD32VF103CBT6開発ボード
■主な仕様
・CPU:GD32VF103CBT6
・メモリ:128KB Flash/32KB SRAM
・160×80ドット、0.96インチのフルカラーIPS液晶
・TFスロット(microSDスロット)

Longan Nano PINOUT
download Longan Nano Datasheet

PlatformIO Core (CLI)

Longan nano で Hello World!
Sipeed Longan Nanoで文字を表示してみる
・図形描画の関数の説明がある
Sipeed Longan Nano 用の、FONTX2対応LCD描画ライブラリとサンプルプログラム

以上

続きを読む "Longan-NanoでADCを使う(framework:gd32v版)"

| | コメント (0)

2020年5月13日 (水)

開発ツールPlatformIOでSipeed-RV(RISC-V)-Debuggerを使用する(Longan-Nano版)

2020/5/13+

PlatformIO Sipeed RV(RISC-V) Debugger Longan Nano

PlatformIO Sipeed RV(RISC-V) Debugger Longan Nano

概要

開発ツールPlatformIOで以下のSipeed-RV(RISC-V)-Debuggerを使用する。ここでは、ターゲット・ボードとして、Longan-Nanoを使用する。 (ホストPCとしてはubuntuを想定している)

Sipeed RISC-V デバッガ
Sipeed USB-JTAG/TTL RISC-V Debugger

PlatformIOのインストール

python3 -m venv pio_env source pio_env/bin/activate pip3 install platformio インストール後も、本ツールを使用する場合 同じディレクトリで以下を実行する: source pio_env/bin/activate # 「source」は、「.」でも良い

準備

以下を実行して、platformio用のudevのrulesを登録する:

curl -fsSL https://raw.githubusercontent.com/platformio/platformio-core/master/scripts/99-platformio-udev.rules | sudo tee /etc/udev/rules.d/99-platformio-udev.rules sudo udevadm control --reload-rules sudo usermod -a -G dialout $USER sudo usermod -a -G plugdev $USER

接続

以下の表のようにターゲット・ボードLongan-Nanoと本デバッガを接続する:

pin# Board JTAG Pin
1 GND
10 TCK
8 TDO
6 TMS
4 RST
2 TDI

Longan-Nanoと接続する際は、コネクタ・ピンとしてRST(Reset)が出ていないので 接続しなくてもかまわない。ただし、ボードをリセットするときは、電源をいったん切る必要がある。 (デバッガーを接続しているときは、Resetボタンは無効になっている)

USBシリアルの機能もあるので、USBシリアルとして以下のように接続する:

pin# Board Serial Pin
9 GND
7 NC
5 RXD(←TXD)
3 TXD(→RXD)
1 GND(JTAGのGNDとしても使用する)

NC: None Connection

ホストPCに本デバッガ(ドングル)を挿すと,/dev/ttyUSB0,/dev/ttyUSB1 と2つのシリアルポートとして認識されるが /dev/ttyUSB1が実際のシリアルポートになる。

サンプル・プログラム

$ cd pio_ws $ . pio_env/bin/activate git clone https://github.com/sipeed/Longan_GD32VF_examples.git cd Longan_GD32VF_examples # 32GB以下のmicroSDに以下の2つのファイルをコピーして、 # そのmicroSDをボードのTFスロットに刺しておく。 $ ls put_into_tf_card/ bmp.bin logo.bin cd Longan_GD32VF_examples/gd32v_lcd nano platformio.ini #以下の内容になるよう編集する:

platformio.ini

; Upload options: custom upload port, speed and extra flags ; Library options: dependencies, extra library storages ; Advanced options: extra scripting ; ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html [env:sipeed-longan-nano] platform = gd32v board = sipeed-longan-nano framework = gd32vf103-sdk #upload_protocol = dfu #debug_tool = sipeed-rv-debugger upload_protocol = sipeed-rv-debugger debug_tool = sipeed-rv-debugger #upload_protocol = um232h #debug_tool = um232h

続き:

pio run -t upload #出力例: $ pio run -t upload Processing sipeed-longan-nano (platform: gd32v; board: sipeed-longan-nano; framework: gd32vf103-sdk) ---------------------------------------------------------------------- Verbose mode can be enabled via `-v, --verbose` option CONFIGURATION: https://docs.platformio.org/page/boards/gd32v/sipeed-longan-nano.html PLATFORM: GigaDevice GD32V 1.1.2 > Sipeed Longan Nano HARDWARE: GD32VF103CBT6 108MHz, 32KB RAM, 128KB Flash DEBUG: Current (sipeed-rv-debugger) External (altera-usb-blaster, gd-link, jlink, rv-link, sipeed-rv-debugger, um232h) PACKAGES: - framework-gd32vf103-sdk 1.0.0 - tool-gd32vflash 0.1.0 - tool-openocd-gd32v 0.1.1 - toolchain-gd32v 9.2.0 LDF: Library Dependency Finder -> http://bit.ly/configure-pio-ldf LDF Modes: Finder ~ chain, Compatibility ~ soft Found 0 compatible libraries Scanning dependencies... No dependencies Building in release mode Checking size .pio/build/sipeed-longan-nano/firmware.elf Advanced Memory Usage is available via "PlatformIO Home > Project Inspect" RAM: [===== ] 47.9% (used 15696 bytes from 32768 bytes) Flash: [== ] 15.7% (used 20632 bytes from 131072 bytes) Configuring upload protocol... AVAILABLE: altera-usb-blaster, gd-link, jlink, rv-link, serial, sipeed-rv-debugger, um232h CURRENT: upload_protocol = sipeed-rv-debugger Uploading .pio/build/sipeed-longan-nano/firmware.elf Open On-Chip Debugger 0.10.0+dev-00911-gcfbca74bd (2019-09-12-09:31) Licensed under GNU GPL v2 For bug reports, read http://openocd.org/doc/doxygen/bugs.html Warn : Transport "jtag" was already selected jtag adapter speed: 1000 kHz Info : clock speed 1000 kHz Info : JTAG tap: riscv.cpu tap/device found: 0x1000563d (mfg: 0x31e (Andes Technology Corporation), part: 0x0005, ver: 0x1) Warn : JTAG tap: riscv.cpu UNEXPECTED: 0x1000563d (mfg: 0x31e (Andes Technology Corporation), part: 0x0005, ver: 0x1) Error: JTAG tap: riscv.cpu expected 1 of 1: 0x1e200a6d (mfg: 0x536 (Nuclei System Technology Co.,Ltd.), part: 0xe200, ver: 0x1) Info : JTAG tap: auto0.tap tap/device found: 0x790007a3 (mfg: 0x3d1 (GigaDevice Semiconductor (Beijing)), part: 0x9000, ver: 0x7) Error: Trying to use configured scan chain anyway... Warn : AUTO auto0.tap - use "jtag newtap auto0 tap -irlen 5 -expected-id 0x790007a3" Warn : Bypassing JTAG setup events due to errors Info : datacount=4 progbufsize=2 Info : Exposing additional CSR 3040 <省略> Info : Exposing additional CSR 3071 Info : Examined RISC-V core; found 1 harts Info : hart 0: XLEN=32, misa=0x40901105 Info : Listening on port 3333 for gdb connections Info : device id = 0x19060410 Info : flash_size_in_kb = 0x00000080 Info : flash size = 128kbytes Info : JTAG tap: riscv.cpu tap/device found: 0x1000563d (mfg: 0x31e (Andes Technology Corporation), part: 0x0005, ver: 0x1) Warn : JTAG tap: riscv.cpu UNEXPECTED: 0x1000563d (mfg: 0x31e (Andes Technology Corporation), part: 0x0005, ver: 0x1) Error: JTAG tap: riscv.cpu expected 1 of 1: 0x1e200a6d (mfg: 0x536 (Nuclei System Technology Co.,Ltd.), part: 0xe200, ver: 0x1) Info : JTAG tap: auto0.tap tap/device found: 0x790007a3 (mfg: 0x3d1 (GigaDevice Semiconductor (Beijing)), part: 0x9000, ver: 0x7) Error: Trying to use configured scan chain anyway... Warn : Bypassing JTAG setup events due to errors ** Programming Started ** ** Programming Finished ** ** Verify Started ** ** Verified OK ** Info : Hart 0 unexpectedly reset! *** [upload] Error 1 ===== [FAILED] Took 6.07 seconds ===== # エラーになっているが「** Verified OK **」となっているので書き込みは正常終了している。 # resetボタンは無効になっているので、いったんUSBケーブルを外してから、再度、接続してボードを再起動する。 # 以上で書き込みが終わり、LCDにデモのアニメーションが表示される。

デバッガとして使用する際は、VScode(+platformio)でplatformio.iniを上の例のように設定して、ソース上の行番号の左側をクリックしてbreakpointを設定して、デバッガを起動(Run/Start Debugging)すると、デバッガとして使用できる。 VScodeは以下のコマンドで起動できる:

code ./project_dir

シリアル・モニタ

プログラム中にprintfなどでシリアル出力しているときは、別の端末で以下のコマンドを実行する:

picocom /dev/ttyUSB1 -b115200 #または pio device monitor --baud 115200

なお、ビットレート115200は、プログラムの設定で変わっている可能性がある。

補足

本デバッガの仕様が以下になっているので、動作確認はしていないが stlinkとして使用できる可能性がある。

仕様 STM32 SWDデバッグインタフェース シンプルな4線式インタフェース(電源を含む) STM8 SWIMダウンロードデバッグ IAR、STVDなどの一般的な開発環境をサポート ST-LINK Utility 2.0および4.2.1 ...

接続は以下のようになると思われる:

pin# Board SWD Pin
1(GND) GND
10(TCK) SWCLK
6(TMS) SWDIO
4(RST) RESET

参考: SWD使用時のNucleoのCN4とCPUピンの対応表

pin# Fuction CPU port
1 VDD_TARGET 3.3V
2 SWCLK PA14
3 GND GND
4 SWDIO PA13
5 NRST RESET
6 SWO NC

参考情報

PIO Unified Debugger » Sipeed RV Debugger
PIO Unified Debugger » ST-LINK

PIO Unified Debugger » UM232H
開発ツールPlatformIOでFT232Hボードをデバッガーとして使用する(Longan-Nano版)

Sipeed Longan Nano RISC-V GD32VF103CBT6開発ボード
■主な仕様
・CPU:GD32VF103CBT6
・メモリ:128KB Flash/32KB SRAM
・160×80ドット、0.96インチのフルカラーIPS液晶
・TFスロット(microSDスロット)

Longan Nano PINOUT
download Longan Nano Datasheet

PlatformIO Core (CLI)

Longan nano で Hello World!
Sipeed Longan Nanoで文字を表示してみる
・図形描画の関数の説明がある
Sipeed Longan Nano 用の、FONTX2対応LCD描画ライブラリとサンプルプログラム

以上

続きを読む "開発ツールPlatformIOでSipeed-RV(RISC-V)-Debuggerを使用する(Longan-Nano版)"

| | コメント (0)

2020年5月 9日 (土)

プログラミング言語RustをLongan-Nanoボードで動かす(GD32VF103CBT6)

2020/5/9

Rust Longan Nano

Rust Longan Nano

概要

プログラミング言語Rustを以下のLongan-Nanoボードで動かす。 (ホストPCとしてはubuntuを想定している)

Sipeed Longan Nano RISC-V GD32VF103CBT6開発ボード

関連ツールのインストール

以下で必要なツールをインスールする:
(1)rustツールのインストール

curl https://sh.rustup.rs -sSf | sh

(2)その他の必要ツールのインストール

sudo apt-get install picocom

Rust(RISC-V)関係ツールのインストール

(1)Installing dependencies

rustup default stable rustup target add riscv32imac-unknown-none-elf rustup default nightly rustup target add riscv32imac-unknown-none-elf

(2)RISC-V toolchain (e.g. from SiFive) install

mkdir riscv-gnu-toolchain cd riscv-gnu-toolchain wget https://static.dev.sifive.com/dev-tools/riscv64-unknown-elf-gcc-8.3.0-2019.08.0-x86_64-linux-ubuntu14.tar.gz tar -zxvf riscv64-unknown-elf-gcc-8.3.0-2019.08.0-x86_64-linux-ubuntu14.tar.gz export PATH=$PATH:~/riscv-gnu-toolchain/riscv64-unknown-elf-gcc-8.3.0-2019.08.0-x86_64-linux-ubuntu14/bin

(3)GD32対応(GD32VF103 support)のdfu-utilのインストール

mkdir gd32-tools cd gd32-tools git clone https://github.com/riscv-mcu/gd32-dfu-utils.git cd gd32-dfu-utils ./autogen.sh ./configure make sudo cp src/dfu-util /opt/bin/gd32-dfu-util export PATH=$PATH:/opt/bin

「dfu-util 0.9」でGD32対応済みの可能性があるが ここでは、GD32でフォークしたものを使用する。

(参考)古いバージョンでの問題点:
https://github.com/riscv-rust/longan-nano/issues/5

以上で、exportとしたものは、.bashrcに登録すること。

Build

以下を実行してサンプルコードをビルドする。

git clone https://github.com/riscv-rust/longan-nano.git cd longa-nano nano .cargo/config 以下のように修正する: "link-arg=-Tmemory-c8.x" → "link-arg=-Tmemory-cb.x" # To build all the provided examples cargo build --examples --release --all-features

make bin & flash it

以下を実行してbin(複数)を作成してボードに書き込む。

riscv64-unknown-elf-objcopy -O binary target/riscv32imac-unknown-none-elf/release/examples/blinky blinky.bin riscv64-unknown-elf-objcopy -O binary target/riscv32imac-unknown-none-elf/release/examples/ferris ferris.bin riscv64-unknown-elf-objcopy -O binary target/riscv32imac-unknown-none-elf/release/examples/display display.bin riscv64-unknown-elf-objcopy -O binary target/riscv32imac-unknown-none-elf/release/examples/hello_world hello_world.bin riscv64-unknown-elf-objcopy -O binary target/riscv32imac-unknown-none-elf/release/examples/scan scan.bin # Keep the BOOT0 button pressed while power-up or # while pressing and releaseing the reset button. #以下から実行したいbinを1つ選び実行する: gd32-dfu-util -a 0 -s 0x08000000:leave -D blinky.bin gd32-dfu-util -a 0 -s 0x08000000:leave -D ferris.bin gd32-dfu-util -a 0 -s 0x08000000:leave -D display.bin gd32-dfu-util -a 0 -s 0x08000000:leave -D hello_world.bin gd32-dfu-util -a 0 -s 0x08000000:leave -D scan.bin

書き込み後、(再起動しないようなので)[reset]ボタンを押すこと。

実行結果

(1)blinky
実行すると、LEDが赤、緑、青と繰り返し変化して光る。

(2)ferris
実行すると、LCDに、ferris(蟹)の画像が表示される。
蟹がRust言語のマスコットになっているらしい。。。

以下にFerrisの画像がある:
https://rustacean.net/

(3)display
実行すると、LCDに" Hello Rust! "のテキストが表示される。

(4)hello_world
USBシリアルを接続して、実行すると以下のように"Hello, world"がシリアルに出力される。

picocom /dev/ttyUSB0 -b115200 Terminal ready Hello, world

(5)scan
USBシリアルを接続して、実行すると以下がシリアルに出力される。

picocom /dev/ttyUSB0 -b115200 Terminal ready scan started 00000000..20007fff 40000000..40017fff 40020000..400207ff 40021000..400213ff 40022000..400223ff 40023000..400233ff 50000000..5003ffff 60000000..a0000fff d1000000..d1000fff d2000000..d200ffff e0000000..efffffff ==> ffffff00 scan finished

なお、USBシリアルは、GNG/T0/R0にクロスで接続する。(3V3は接続しない)

参照情報

Sipeed Longan Nano RISC-V GD32VF103CBT6開発ボード

The Embedded Rust Book
The Embedded Rust Book - Semihosting

UbuntuにRustをインストールする

以上

続きを読む "プログラミング言語RustをLongan-Nanoボードで動かす(GD32VF103CBT6)"

| | コメント (0)

2020年5月 3日 (日)

Longan-NanoのLCDに動画を表示する

2020/5/4
改良コード追加

2020/5/3++
初版

PlatformIO Longan Nano Movies

PlatformIO Longan Nano Movies

概要

Longan-NanoのLCDに動画を表示する。SDKのサンプルコードを流用して動画を再生してみる。 (ホストPCとしてはubuntuを想定している)

PlatformIOのインストール

python3 -m venv pio_env source pio_env/bin/activate pip3 install platformio インストール後も、本ツールを使用する場合 同じディレクトリで以下を実行する: source pio_env/bin/activate # 「source」は、「.」でも良い

準備

以下を実行して、udevのrulesを登録する:

curl -fsSL https://raw.githubusercontent.com/platformio/platformio-core/master/scripts/99-platformio-udev.rules | sudo tee /etc/udev/rules.d/99-platformio-udev.rules sudo udevadm control --reload-rules sudo usermod -a -G dialout $USER sudo usermod -a -G plugdev $USER

サンプル・コード

このサンプル・コードを利用してLCDに動画を表示する。 本プログラム自身には手を加えず、動画データであるbmp.binを差し替えることで任意の動画を表示する。 以下の手順で、サンプル・コードをビルド&実行してみる。

$ cd pio_ws $ . pio_env/bin/activate git clone https://github.com/sipeed/Longan_GD32VF_examples.git cd Longan_GD32VF_examples # 32GB以下のmicroSDに以下の2つのファイルをコピーして、 # そのmicroSDをボードのTFスロットに刺しておく。 $ ls put_into_tf_card/ bmp.bin logo.bin cd Longan_GD32VF_examples/gd32v_lcd pio run # ボードをPCに接続して # 以下の手順で書き込みモード(DFUモード)にする: # (1)「BOOT」ボタンと「RESET」ボタンも同時に押す。 # (2)1秒くらいして「RESET」ボタンを離す。 # (3)その後、「BOOT」も離す。 pio run -t upload # 以上で書き込みが終わり、LCDにデモのアニメーションが表示される。

次で、差し替えるためのbmp.binの作成方法を説明する。

動画データbmp.binの作成

ツールのインストールとパッチ:

sudo apt-get install ffmpeg mkdir BMP_ws cd BMP_ws curl -OL http://dl.sipeed.com/LONGAN/Nano/Firmware/badapple_demo_tools/tools_bmp2hex.zip unzip tools_bmp2hex.zip # unzipで以下のファイルに解凍される: # bmp2hex.py genhex.py rename.py # genhex.pyは以下のように修正する: # (rename.pyは使用しない)

以下のように修正(patch)する:
genhex.py

import os import sys # for cli args args = sys.argv # get args if os.path.exists('bmp.bin'): os.remove('bmp.bin') num = 1 while num <= int(args[1]): #while num <= 2189: #os.system("python.exe bmp2hex.py -kbin {0}.bmp".format(num)) os.system("python3 bmp2hex.py -kbin {:04d}.bmp".format(num)) num += 1

素材となる動画ファイルをダウンロード(入手)する:
(素材となる動画ファイルは任意である)

#以下のうち、1つを実行して素材をダウンロードする: wget https://archive.org/download/BigBuckBunny_310/big_buck_bunny_640.mp4 wget https://archive.org/download/Sintel/sintel-2048-stereo.mp4 wget http://ftp.nluug.nl/pub/graphics/blender/demo/movies/ToS/tears_of_steel_720p.mov

以下の手順で動画ファイルをbmp.binに変換する:
(ここでは素材例としてbig_buck_bunny_640.mp4を使う)

cd BMP_ws # *.bmpがあれば削除する rm *.bmp # 以下を実行して、動画のフレームをBMPファイルに変換する: #(複数のBMPファイルができる) ffmpeg -ss 10 -t 300 -i big_buck_bunny_640.mp4 -r 12 -s 160x80 -vcodec bmp %04d.bmp # 上の例では「 -ss 10 -t 300」で頭出し10秒後、再生時間を300秒を指定しているが任意である。 # 留意点:数多くのBMPファイルを作るので時間がかかる # 実行が完了したら、<フレーム番号>.bmpの出来上がるので、最大の番号を確認する。 # 例えば、それが 9999 ならば # 以下を実行する: python3 genhex.py 9999 # 各bmpファイルを結合して,bmp.binを作成するので # (時間がかかるので)終わるまで待つ。

以上で作成したbmp.binをLongan-Nanoに挿しているmicroSDにコピーする。 ボードを再起動すると、ロゴ画面に続いて、bmp.binに変換した動画が再生される。

補足

(1)動画の最大フレーム数が大きいと最後まで再生されないことがある。
そのときは、main.cの以下の部分のフレーム数の設定を以下のように変更する。
行番号87「for (int i=0; i<2189;i++)」→「for (int i=0; i<9999;i++)」

最大フレーム数よりも大きい値を設定しておくとプログラムをいちいち変更せずに済むので便利だと思う。 (ただし、最終的にエラーで止まるので、オリジナルのように繰り返し実行しなくなるが リセットボタンを押せば、再実行するので、特に問題ないと思う)

(2)デモプログラムの流用なので時間軸の制御はしていない。
設定としては、fpsは12だが、実際には、それよりも多少速く再生されるようだ。

(3)最終フレームの画像が半分になって上下に表示されるバグがあるようだ。
回避策として、最終フレームとして全画面黒一色のものを追加して、bmp.binを作ることで回避できそうだ。

改良コード

ロゴ画面を表示しているときにBOOT0ボタンを押し続けると、別のファイル(bmp2.bin)を選択して 動画を表示する機能を追加した。

以下にmain.cの差分を記載する:
(1)main(void)の中のGPIO初期化部分を以下のように変更する:
(「PA8を入力モードに初期化する」を追加する)

rcu_periph_clock_enable(RCU_GPIOA); rcu_periph_clock_enable(RCU_GPIOC); // init for LED_R(PC13) gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13); // init for LED_G(PA1),LED_B(PA2) gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1|GPIO_PIN_2); // configure BOOT0 button pin(PA8) as input gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_8); init_uart0();

(2)main(void)の"bmp.bin"をopenする部分を以下のように変更する:

f_close(&fil); // check whether the BOOT0 button is pressed if(RESET == gpio_input_bit_get(GPIOA, GPIO_PIN_8)){ // select default fr = f_open(&fil, "bmp.bin", FA_READ); } else { // select 2nd option fr = f_open(&fil, "bmp2.bin", FA_READ); }; //fr = f_open(&fil, "bmp.bin", FA_READ);

参考情報

ffmpeg:
ffmpegの使い方
それFFmpegで出来るよ!
ffmpeg使い方のまとめ
Re:ゼロから始めるFFmpeg
FFmpegで秒数を指定して動画を切り出すワンライナー

Sipeed Longan Nano RISC-V GD32VF103CBT6開発ボード
■主な仕様
・CPU:GD32VF103CBT6
・メモリ:128KB Flash/32KB SRAM
・160×80ドット、0.96インチのフルカラーIPS液晶
・TFスロット(microSDスロット)

Longan Nano PINOUT
download Longan Nano Datasheet

LonganNanoで「RISC-Vちょっとできる」になろう!(ハードウェア編)
GD32VF103 RISC-V

PlatformIO Core (CLI)

Longan nano で Hello World!
Sipeed Longan Nanoで文字を表示してみる
・図形描画の関数の説明がある
Sipeed Longan Nano 用の、FONTX2対応LCD描画ライブラリとサンプルプログラム

開発ツールPlatformIOでFT232Hボードをデバッガーとして使用する(Longan-Nano版)

Sipeed Longan Nano(その4)

以上

続きを読む "Longan-NanoのLCDに動画を表示する"

| | コメント (0)

2020年5月 2日 (土)

開発ツールPlatformIOでFT232Hボードをデバッガーとして使用する(Longan-Nano版)

2020/5/2

PlatformIO um232h Longan Nano

PlatformIO um232h Longan Nano

概要

開発ツールPlatformIOで以下のFT232Hボードをデバッガーとして使用する。ここでは、ターゲット・ボードとして、Longan-Nanoを使用する。 (ホストPCとしてはubuntuを想定している)

FT232H使用USB⇔GPIO+SPI+I2C変換モジュール

PlatformIOのインストール

python3 -m venv pio_env source pio_env/bin/activate pip3 install platformio インストール後も、本ツールを使用する場合 同じディレクトリで以下を実行する: source pio_env/bin/activate # 「source」は、「.」でも良い

準備

以下を実行して、platformio用のudevのrulesを登録する:

curl -fsSL https://raw.githubusercontent.com/platformio/platformio-core/master/scripts/99-platformio-udev.rules | sudo tee /etc/udev/rules.d/99-platformio-udev.rules sudo udevadm control --reload-rules sudo usermod -a -G dialout $USER sudo usermod -a -G plugdev $USER

以下を実行して、FT232H用のudevのrulesを登録する:

sudo apt-get install libusb-1.0 sudo nano /etc/udev/rules.d/11-ftdi.rules #以下の内容を作成する: # /etc/udev/rules.d/11-ftdi.rules SUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTR{idProduct}=="6001", GROUP="plugdev", MODE="0666" SUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTR{idProduct}=="6011", GROUP="plugdev", MODE="0666" SUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTR{idProduct}=="6010", GROUP="plugdev", MODE="0666" SUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTR{idProduct}=="6014", GROUP="plugdev", MODE="0666" SUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTR{idProduct}=="6015", GROUP="plugdev", MODE="0666" sudo udevadm control --reload-rules sudo usermod -a -G plugdev $USER

接続

以下の表のようにターゲット・ボードLongan-NanoとFT232Hボードを接続する:

FT232H Pin Longan-Nano JTAG Pin Description
GND GND Digital ground
D0 JTCK JTAG Return Test Clock
D1 JTDI Test Data In
D2 JTDO Test Data Out
D3 JTMS Test Mode State

サンプル・プログラム

$ cd pio_ws $ . pio_env/bin/activate git clone https://github.com/sipeed/Longan_GD32VF_examples.git cd Longan_GD32VF_examples # 32GB以下のmicroSDに以下の2つのファイルをコピーして、 # そのmicroSDをボードのTFスロットに刺しておく。 $ ls put_into_tf_card/ bmp.bin logo.bin cd Longan_GD32VF_examples/gd32v_lcd nano platformio.ini #以下の内容になるよう編集する:

platformio.ini

;PlatformIO Project Configuration File ; ; Build options: build flags, source filter ; Upload options: custom upload port, speed and extra flags ; Library options: dependencies, extra library storages ; Advanced options: extra scripting ; ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html [env:sipeed-longan-nano] platform = gd32v board = sipeed-longan-nano framework = gd32vf103-sdk #upload_protocol = dfu #debug_tool = sipeed-rv-debugger upload_protocol = um232h debug_tool = um232h

続き:

pio run -t upload #出力例:
$ pio run -t upload Processing sipeed-longan-nano (platform: gd32v; board: sipeed-longan-nano; framework: gd32vf103-sdk) -------------------------------------------------------------------------------------------------------------------------------------------------- Verbose mode can be enabled via `-v, --verbose` option CONFIGURATION: https://docs.platformio.org/page/boards/gd32v/sipeed-longan-nano.html PLATFORM: GigaDevice GD32V 1.1.2 > Sipeed Longan Nano HARDWARE: GD32VF103CBT6 108MHz, 32KB RAM, 128KB Flash DEBUG: Current (um232h) External (altera-usb-blaster, gd-link, jlink, rv-link, sipeed-rv-debugger, um232h) PACKAGES: - framework-gd32vf103-sdk 1.0.0 - tool-gd32vflash 0.1.0 - tool-openocd-gd32v 0.1.1 - toolchain-gd32v 9.2.0 LDF: Library Dependency Finder -> http://bit.ly/configure-pio-ldf LDF Modes: Finder ~ chain, Compatibility ~ soft Found 0 compatible libraries Scanning dependencies... No dependencies Building in release mode Checking size .pio/build/sipeed-longan-nano/firmware.elf Advanced Memory Usage is available via "PlatformIO Home > Project Inspect" RAM: [= ] 7.1% (used 2328 bytes from 32768 bytes) Flash: [= ] 12.3% (used 16160 bytes from 131072 bytes) Configuring upload protocol... AVAILABLE: altera-usb-blaster, gd-link, jlink, rv-link, serial, sipeed-rv-debugger, um232h CURRENT: upload_protocol = um232h Uploading .pio/build/sipeed-longan-nano/firmware.elf Open On-Chip Debugger 0.10.0+dev-00911-gcfbca74bd (2019-09-12-09:31) Licensed under GNU GPL v2 For bug reports, read http://openocd.org/doc/doxygen/bugs.html jtag adapter speed: 8000 kHz Info : clock speed 8000 kHz Info : JTAG tap: riscv.cpu tap/device found: 0x1000563d (mfg: 0x31e (Andes Technology Corporation), part: 0x0005, ver: 0x1) Warn : JTAG tap: riscv.cpu UNEXPECTED: 0x1000563d (mfg: 0x31e (Andes Technology Corporation), part: 0x0005, ver: 0x1) Error: JTAG tap: riscv.cpu expected 1 of 1: 0x1e200a6d (mfg: 0x536 (Nuclei System Technology Co.,Ltd.), part: 0xe200, ver: 0x1) Info : JTAG tap: auto0.tap tap/device found: 0x790007a3 (mfg: 0x3d1 (GigaDevice Semiconductor (Beijing)), part: 0x9000, ver: 0x7) Error: Trying to use configured scan chain anyway... Warn : AUTO auto0.tap - use "jtag newtap auto0 tap -irlen 5 -expected-id 0x790007a3" Warn : Bypassing JTAG setup events due to errors Info : datacount=4 progbufsize=2 Info : Exposing additional CSR 3040 Info : Exposing additional CSR 3041 ... <省略> ... Info : Exposing additional CSR 3071 Info : Examined RISC-V core; found 1 harts Info : hart 0: XLEN=32, misa=0x40901105 Info : Listening on port 3333 for gdb connections Info : device id = 0x19060410 Info : flash_size_in_kb = 0x00000080 Info : flash size = 128kbytes Info : JTAG tap: riscv.cpu tap/device found: 0x1000563d (mfg: 0x31e (Andes Technology Corporation), part: 0x0005, ver: 0x1) Warn : JTAG tap: riscv.cpu UNEXPECTED: 0x1000563d (mfg: 0x31e (Andes Technology Corporation), part: 0x0005, ver: 0x1) Error: JTAG tap: riscv.cpu expected 1 of 1: 0x1e200a6d (mfg: 0x536 (Nuclei System Technology Co.,Ltd.), part: 0xe200, ver: 0x1) Info : JTAG tap: auto0.tap tap/device found: 0x790007a3 (mfg: 0x3d1 (GigaDevice Semiconductor (Beijing)), part: 0x9000, ver: 0x7) Error: Trying to use configured scan chain anyway... Warn : Bypassing JTAG setup events due to errors ** Programming Started ** ** Programming Finished ** ** Verify Started ** ** Verified OK ** Info : Hart 0 unexpectedly reset! *** [upload] Error 1 ===== [FAILED] Took 2.91 seconds ==== # エラーになっているが「** Verified OK **」となっているので書き込みは正常終了している。 # resetボタンは無効になっているので、いったんUSBケーブルを外してから、再度、接続してボードを再起動する。 # 以上で書き込みが終わり、LCDにデモのアニメーションが表示される。

デバッガーとして使用する際は、VScode(+platformio)でplatformio.iniを上の例のように設定して、ソース上の行番号の左側をクリックしてbreakpointを設定して、デバッガーを起動(Run/Start Debuggin)すると、デバッガーとして使用できる。

参考情報

PIO Unified Debugger » UM232H

Sipeed Longan Nano RISC-V GD32VF103CBT6開発ボード
■主な仕様
・CPU:GD32VF103CBT6
・メモリ:128KB Flash/32KB SRAM
・160×80ドット、0.96インチのフルカラーIPS液晶
・TFスロット(microSDスロット)

Longan Nano PINOUT
download Longan Nano Datasheet

PlatformIO Core (CLI)

Longan nano で Hello World!
Sipeed Longan Nanoで文字を表示してみる
・図形描画の関数の説明がある
Sipeed Longan Nano 用の、FONTX2対応LCD描画ライブラリとサンプルプログラム

以上

続きを読む "開発ツールPlatformIOでFT232Hボードをデバッガーとして使用する(Longan-Nano版)"

| | コメント (0)

2020年4月30日 (木)

開発ツールPlatformIOをcli(comand line interface)で使う(Longan-Nano版)

2020/4/30

PlatformIO cli Longan Nano

PlatformIO cli Longan Nano

概要

開発ツールPlatformIOをcli(comand line interface)で使う(Longan-Nano版)。VisualCodeのプラグインとしてPlatformIOを使用することができるが、ここでは、cliとしての使い方について記する。 (ホストPCとしてはubuntuを想定している)

PlatformIOのインストール

python3 -m venv pio_env source pio_env/bin/activate pip3 install platformio インストール後も、本ツールを使用する場合 同じディレクトリで以下を実行する: source pio_env/bin/activate # 「source」は、「.」でも良い

準備

以下を実行して、udevのrulesを登録する:

curl -fsSL https://raw.githubusercontent.com/platformio/platformio-core/master/scripts/99-platformio-udev.rules | sudo tee /etc/udev/rules.d/99-platformio-udev.rules sudo udevadm control --reload-rules sudo usermod -a -G dialout $USER sudo usermod -a -G plugdev $USER

テスト用プロジェクト sample-longan を作成/実行する

#ターゲットボードのtarget名を検索する # (ここではlonganを検索する) pio boards | grep -i logan #出力例: sipeed-longan-nano GD32VF103CBT6 108MHz 128KB 32KB Sipeed Longan Nano sipeed-longan-nano-lite GD32VF103C8T6 108MHz 64KB 20KB Sipeed Longan Nano Lite # flash容量が2種類あるが、持っているボードのものを選択する # 128kBのほうを持っているので、target名としては「sipeed-longan-nano」になる。 # プロジェクト sample-longan のディレクトリを作成する mkdir sample-longan cd sample-longan # 以下を実行して必要なファイルを作成する pio init --board sipeed-longan-nano nano platformio.ini # arduinoとして動かすので、以下の内容になるように編集する:
; PlatformIO Project Configuration File ; ; Build options: build flags, source filter ; Upload options: custom upload port, speed and extra flags ; Library options: dependencies, extra library storages ; Advanced options: extra scripting ; ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html [env:sipeed-longan-nano] platform = gd32v board = sipeed-longan-nano #framework = gd32vf103-sdk framework = arduino upload_protocol = dfu

続き:

# テスト用のmain.cppを作成する nano src/main.cpp 次の内容に編集する:
/* * Blink * Turns on an LED on for one second, * then off for one second, repeatedly. */ #include <Arduino.h> // Set LED_BUILTIN if it is not defined by Arduino framework // #define LED_BUILTIN 2 void setup() { // initialize LED digital pin as an output. pinMode(LED_BUILTIN, OUTPUT); } void loop() { // turn the LED on (HIGH is the voltage level) digitalWrite(LED_BUILTIN, HIGH); // wait for a second delay(1000); // turn the LED off by making the voltage LOW digitalWrite(LED_BUILTIN, LOW); // wait for a second delay(1000); }

続き:

# build pio run # ボードをPCに接続して # 以下の手順で書き込みモード(DFUモード)にする: # (1)「BOOT」ボタンと「RESET」ボタンも同時に押す。 # (2)1秒くらいして「RESET」ボタンを離す。 # (3)その後、「BOOT」も離す。 # build&upload(flash) pio run -t upload # buildしないで書き込む場合は以下を実行する: pio run -t nobuild -t upload -v # -v は、詳細を表示するオプション # 以上で、基本的な操作としては完了となる

書き込み後、LEDが点滅すれば動作としてはOKとなる。

sample#1

sample#1は、arduinoではなくgd32vf103-sdkによるLED点滅プログラムになる:

git clone https://github.com/sipeed/platform-gd32v.git cd platform-gd32v/examples/longan-nano-blink # platformio.iniの内容の確認: # 以下のようであればOK

platformio.ini

; PlatformIO Project Configuration File ; ; Build options: build flags, source filter, extra scripting ; Upload options: custom port, speed and extra flags ; Library options: dependencies, extra library storages ; ; Please visit documentation for the other options and examples ; http://docs.platformio.org/page/projectconf.html [env:sipeed-longan-nano] platform = gd32v framework = gd32vf103-sdk board = sipeed-longan-nano monitor_speed = 115200 upload_protocol = dfu

続き:

# ボードをPCに接続して # 以下の手順で書き込みモード(DFUモード)にする: # (1)「BOOT」ボタンと「RESET」ボタンも同時に押す。 # (2)1秒くらいして「RESET」ボタンを離す。 # (3)その後、「BOOT」も離す。 pio run -t upload # 以上で書き込みが終わり、LEDが点滅する。

sample#2

sample#2は、LCDにアニメーションを表示するデモになる:

$ cd pio_ws $ . pio_env/bin/activate git clone https://github.com/sipeed/Longan_GD32VF_examples.git cd Longan_GD32VF_examples # 32GB以下のmicroSDに以下の2つのファイルをコピーして、 # そのmicroSDをボードのTFスロットに刺しておく。 $ ls put_into_tf_card/ bmp.bin logo.bin cd Longan_GD32VF_examples/gd32v_lcd pio run # ボードをPCに接続して # 以下の手順で書き込みモード(DFUモード)にする: # (1)「BOOT」ボタンと「RESET」ボタンも同時に押す。 # (2)1秒くらいして「RESET」ボタンを離す。 # (3)その後、「BOOT」も離す。 pio run -t upload # 以上で書き込みが終わり、LCDにデモのアニメーションが表示される。

sample#3

sample#3は、helloworld的なプログラムになる:

# 既存のプロジェクトをコピーしてhelloworld的なプログラムを作る cp -r gd32v_lcd gd32v_hello cd gd32v_hello

platformio.iniの確認:
以下のようであればOK

;PlatformIO Project Configuration File ; ; Build options: build flags, source filter ; Upload options: custom upload port, speed and extra flags ; Library options: dependencies, extra library storages ; Advanced options: extra scripting ; ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html [env:sipeed-longan-nano] platform = gd32v board = sipeed-longan-nano framework = gd32vf103-sdk upload_protocol = dfu debug_tool = sipeed-rv-debugge

以下のプログラムを作成する:
src/main.c

#include "lcd/lcd.h" #include "fatfs/tf_card.h" #include <string.h> void init_uart0(void) { /* enable GPIO clock */ rcu_periph_clock_enable(RCU_GPIOA); /* enable USART clock */ rcu_periph_clock_enable(RCU_USART0); /* connect port to USARTx_Tx */ gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); /* connect port to USARTx_Rx */ gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10); /* USART configure */ usart_deinit(USART0); usart_baudrate_set(USART0, 115200U); usart_word_length_set(USART0, USART_WL_8BIT); usart_stop_bit_set(USART0, USART_STB_1BIT); usart_parity_config(USART0, USART_PM_NONE); usart_hardware_flow_rts_config(USART0, USART_RTS_DISABLE); usart_hardware_flow_cts_config(USART0, USART_CTS_DISABLE); usart_receive_config(USART0, USART_RECEIVE_ENABLE); usart_transmit_config(USART0, USART_TRANSMIT_ENABLE); usart_enable(USART0); usart_interrupt_enable(USART0, USART_INT_RBNE); } int _put_char(int ch) { usart_data_transmit(USART0, (uint8_t) ch ); while ( usart_flag_get(USART0, USART_FLAG_TBE)== RESET){ } return ch; } int main(void) { rcu_periph_clock_enable(RCU_GPIOA); rcu_periph_clock_enable(RCU_GPIOC); gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13); gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1|GPIO_PIN_2); init_uart0(); LEDR(1); LEDG(1); LEDB(1); Lcd_Init(); // init OLED LCD_Clear(BLACK); BACK_COLOR=BLACK; LCD_ShowString(0, 0, (u8 *)("Hello World! #0"), WHITE); LCD_ShowString(0, 16, (u8 *)("Hello World! #1"), BLUE); LCD_ShowString(0, 32, (u8 *)("Hello World! #2"), BRED); LCD_ShowString(0, 48, (u8 *)("Hello World! #3"), GBLUE); LCD_ShowString(0, 64, (u8 *)("Hello World! #4"), RED); Draw_Circle(120,40,25,RED); LCD_DrawLine(120,0,130,80,BLUE); LCD_DrawRectangle(120,40,159,79,GREEN); LCD_Fill(130,20,150,30,MAGENTA); printf("printf test start...\n\r"); int n = 0; while (1) { printf("printf test %d\n\r", (int)n); n++; LEDR_TOG; delay_1ms(200); LEDG_TOG; delay_1ms(200); LEDB_TOG; delay_1ms(200); } }

続き:

# ボードをPCに接続して # 以下の手順で書き込みモード(DFUモード)にする: # (1)「BOOT」ボタンと「RESET」ボタンも同時に押す。 # (2)1秒くらいして「RESET」ボタンを離す。 # (3)その後、「BOOT」も離す。 pio run -t upload # 書き込むと、LCDに"Hello World!"の文字列と図形が表示され、LEDが色を変えながら点滅する・ # USBserialを接続していると以下のような文字列が表示される。 # (USBserialは、ボードのGND,T0,R0とクロス接続する) $ picocom /dev/ttyUSB0 -b115200 # /dev/ttyUSB0は、自分の環境に合わせること printf test start... printf test 0 printf test 1 printf test 2 printf test 3 printf test 4 printf test 5 printf test 6 printf test 7 ....

参考情報

Sipeed Longan Nano RISC-V GD32VF103CBT6開発ボード
■主な仕様
・CPU:GD32VF103CBT6
・メモリ:128KB Flash/32KB SRAM
・160×80ドット、0.96インチのフルカラーIPS液晶
・TFスロット(microSDスロット)

Longan Nano PINOUT
download Longan Nano Datasheet

PlatformIO Core (CLI)

Longan nano で Hello World!
Sipeed Longan Nanoで文字を表示してみる
・図形描画の関数の説明がある
Sipeed Longan Nano 用の、FONTX2対応LCD描画ライブラリとサンプルプログラム

以上

続きを読む "開発ツールPlatformIOをcli(comand line interface)で使う(Longan-Nano版)"

| | コメント (0)