SPRESENSEで自作MQTTパブリッシャを動作させてみようと下記の「MqttPublisherTest.ino」というプログラムを実行したところ, 次のようなエラーに出くわしました.
mosquitto New connection from 192.168.1.21 on port 1883. Clienthas exceeded timeout, disconnecting.
シリアルモニタ failed, rc=-4
どうも使っているライブラリが怪しかったので,「SparkfunESP8286WiFi.cpp」というコードの(1)の部分を修正したところ, 上記のエラーは解消することができました, ただ,今度は次のような別のエラーが表示されるようになりました.
mosquitto New connection from 192.168.1.21 on port 1883. New client connected from 192.168.1.21 as ESP8266Client-0 (p2, c1, k15). Socket error on client ESP8266Client-0, disconnecting.
シリアルモニタ failed, rc=-1
処理内容を細かくチェックすると,MQTTブローカからの返信を処理していない箇所があったので, 「SparkfunESP8286WiFi.cpp」というコードの(2)の部分を追加したところ, シリアルモニタのエラーは解消することができました.
mosquitto New connection from 192.168.1.21 on port 1883. New client connected from 192.168.1.21 as ESP8266Client-5165 (p2, c1, k15). Socket error on client ESP8266Client-5165, disconnecting.
シリアルモニタ なし
引き続き処理内容を細かくチェックすると,コネクションの状態を返すべきところがWiFiの接続状態を返している箇所があったので, 「SparkfunESP8286Client.cpp」というコードの(3)の部分を修正したところ, ようやく期待通りの動作をさせることができました.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <SparkFunESP8266WiFi.h> | |
#include <PubSubClient.h> | |
const char* SSID = "XXXXXXXXXXXXX"; | |
const char* PSK = "XXXXXXXXXXXXX"; | |
const char* MQTT_BROKER = "X.X.X.X"; | |
const int MQTT_PORT = 1883; | |
const char* MQTT_TOPIC = "suppa"; | |
const int PUBLISH_INTERVAL_SEC = 1; | |
const int MAX_NUMBER_OF_PUBLISH = 60; | |
ESP8266Client client; | |
PubSubClient mqttClient(client); | |
time_t lastPublishedTime = 0; | |
int numberOfPublishes = 0; | |
void setup() { | |
Serial.begin(115200); | |
initializeESP8266(); | |
connectESP8266(); | |
displayConnectInfo(); | |
mqttClient.setServer(MQTT_BROKER, MQTT_PORT); | |
} | |
void loop() { | |
if (!mqttClient.connected()) { | |
reconnect(); | |
} | |
mqttClient.loop(); | |
time_t currentTime = time(NULL); | |
if (currentTime - lastPublishedTime >= 1) { | |
numberOfPublishes++; | |
String payload(numberOfPublishes, DEC); | |
mqttClient.publish(MQTT_TOPIC, payload.c_str()); | |
lastPublishedTime = currentTime; | |
} | |
} | |
void initializeESP8266() { | |
if (!esp8266.begin(115200)){ | |
Serial.println(F("Error talking to ESP8266.")); | |
exit(0); | |
} | |
Serial.println(F("ESP8266 Shield Present")); | |
} | |
void connectESP8266() { | |
if (esp8266.getMode() != ESP8266_MODE_STA) { | |
if (esp8266.setMode(ESP8266_MODE_STA) < 0) { | |
Serial.println(F("Error setting mode.")); | |
exit(0); | |
} | |
} | |
Serial.println(F("Mode set to station")); | |
if (esp8266.status() <= 0) { | |
Serial.print(F("Connecting to ")); | |
Serial.println(SSID); | |
if (esp8266.connect(SSID, PSK) < 0) { | |
Serial.println(F("Error connecting")); | |
exit(0); | |
} | |
} | |
} | |
void displayConnectInfo() { | |
char connectedSSID[24]; | |
memset(connectedSSID, 0, 24); | |
int retVal = esp8266.getAP(connectedSSID); | |
if (retVal > 0) { | |
Serial.print(F("Connected to: ")); | |
Serial.println(connectedSSID); | |
} | |
IPAddress myIP = esp8266.localIP(); | |
Serial.print(F("My IP: ")); Serial.println(myIP); | |
} | |
void reconnect() { | |
while (!mqttClient.connected()) { | |
Serial.print("Attempting MQTT connection..."); | |
String clientId = "ESP8266Client-"; | |
clientId += String(random(0xffff), HEX); | |
if (mqttClient.connect(clientId.c_str())) { | |
Serial.println("connected"); | |
} | |
else { | |
Serial.print("failed, rc="); | |
Serial.print(mqttClient.state()); | |
exit(0); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
uint8_t ESP8266Client::connected() | |
{ | |
// If data is available, assume we're connected. Otherwise, | |
// we'll try to send the status query, and will probably end | |
// up timing out if data is still coming in. | |
/* modified part (2) | |
status()の戻り値はWiFiの接続状態で,コネクションの状態ではない.. | |
このため,コネクションが張られているにも関わらず, | |
張られていないと判断されて自らコネクションをクローズしてしまっていた感じ. | |
コネクションの接続状態を示すesp8266._status.statをチェックして接続中であれば1を返すコードを追加. | |
if (_socket == ESP8266_SOCK_NOT_AVAIL) | |
return 0; | |
else if (available() > 0) | |
return 1; | |
else if (status() == ESP8266_STATUS_CONNECTED) | |
return 1; | |
*/ | |
int clientStatus = status(); | |
if (_socket == ESP8266_SOCK_NOT_AVAIL) | |
return 0; | |
else if (available() > 0) | |
return 1; | |
else if (esp8266._status.stat == ESP8266_STATUS_CONNECTED) { | |
return 1; | |
} | |
else if (clientStatus == ESP8266_STATUS_CONNECTED) | |
return 1; | |
/* modified part (2) */ | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
int16_t ESP8266Class::tcpSend(uint8_t linkID, const uint8_t *buf, size_t size) | |
{ | |
if (size > 2048) | |
return ESP8266_CMD_BAD; | |
char params[8]; | |
sprintf(params, "%d,%d", linkID, size); | |
sendCommand(ESP8266_TCP_SEND, ESP8266_CMD_SETUP, params); | |
int16_t rsp = readForResponses(RESPONSE_OK, RESPONSE_ERROR, COMMAND_RESPONSE_TIMEOUT); | |
//if (rsp > 0) | |
if (rsp != ESP8266_RSP_FAIL) | |
{ | |
/* modified part (1) | |
print関数だと,buf内のデータをテキストとして扱ってしまい,その内容を改変してしまうケースがあるみたい. | |
buf内のデータをそのまま送信するには,write関数を使えば良いみたいなので,それに置き換え */ | |
/* print((const char *)buf);.*/ | |
_serial->write(buf, size); | |
/* modified part (1) */ | |
rsp = readForResponse("SEND OK", COMMAND_RESPONSE_TIMEOUT); | |
/* added part (2) | |
readForResponse関数でデータを送信すると, | |
例えば「+IPD,0,4:XXXX」というようなレスポンスが届く場合があるが, | |
オリジナルのコードでは,それに対処する処理がなかったので, | |
後続する処理でレスポンスの受け取りに失敗していた模様. | |
そのため,レスポンスに対処するコードを追加. | |
*/ | |
char responseHeader[] = "\r\n\r\n+IPD,,"; | |
int responseHeaderIndex = 0; | |
int state = 1; | |
long timeIn = millis(); | |
int dataSize = 0; | |
while (1) { | |
if (millis() > timeIn + 500) { | |
break; | |
} | |
if (_serial->available()) { | |
rsp += readByteToBuffer(); | |
char c = esp8266RxBuffer[bufferHead - 1]; | |
switch (state) { | |
case 1: | |
case 2: | |
case 3: | |
case 4: | |
case 5: | |
case 6: | |
case 7: | |
case 8: | |
case 9: | |
case 11: | |
if (c == responseHeader[responseHeaderIndex]) { | |
responseHeaderIndex++; | |
state++; | |
} | |
else { | |
state = 0; | |
} | |
break; | |
case 10: | |
if (c == linkID + '0') { | |
state = 11; | |
} | |
else { | |
state = 0; | |
} | |
break; | |
case 12: | |
if (c == ':') { | |
state = 13; | |
} | |
else { | |
dataSize = dataSize * 10 + (c - '0'); | |
} | |
break; | |
} | |
if (state == 0 || state == 13) { | |
break; | |
} | |
} | |
} | |
/* added part (2) */ | |
if (rsp > 0) | |
return size; | |
} | |
return rsp; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
uint8_t ESP8266Client::connected() | |
{ | |
// If data is available, assume we're connected. Otherwise, | |
// we'll try to send the status query, and will probably end | |
// up timing out if data is still coming in. | |
/* modified part (3) | |
status()の戻り値はWiFiの接続状態で,コネクションの状態ではない.. | |
このため,コネクションが張られているにも関わらず, | |
張られていないと判断されて自らコネクションをクローズしてしまっていた感じ. | |
コネクションの接続状態を示すesp8266._status.statをチェックして接続中であれば1を返すコードを追加. | |
if (_socket == ESP8266_SOCK_NOT_AVAIL) | |
return 0; | |
else if (available() > 0) | |
return 1; | |
else if (status() == ESP8266_STATUS_CONNECTED) | |
return 1; | |
*/ | |
int clientStatus = status(); | |
if (_socket == ESP8266_SOCK_NOT_AVAIL) | |
return 0; | |
else if (available() > 0) | |
return 1; | |
else if (esp8266._status.stat == ESP8266_STATUS_CONNECTED) { | |
return 1; | |
} | |
else if (clientStatus == ESP8266_STATUS_CONNECTED) | |
return 1; | |
/* modified part (3) */ | |
return 0; | |
} |