IoT sensor/controller using STM32, W5500 ethernet, MQTT
Dependencies: mbed WIZnet_Library Watchdog DHT MQTT DS1820
main.cpp
- Committer:
- Geekshow
- Date:
- 2020-03-04
- Revision:
- 14:0a3c670b3862
- Parent:
- 12:bcb38c1af703
File content as of revision 14:0a3c670b3862:
#include "mbed.h" #include "Watchdog.h" //#include "rtos.h" //#include "pins.h" #include "WIZnetInterface.h" #include "MQTTSocket.h" #include "MQTTClient.h" #define VERSION "v12" // ========== PIN DEFINITIONS ============ // TODO move pin definitions into separate file #define LED_GREEN PA_5 #define LED_ORANGE PA_1 // Don't use! Shared with D3 #define BUTTON PC_9 #define A_0 PC_0 // Analogue Input 0 #define A_1 PC_1 // Analogue Input 1 #define A_2 PC_2 // Analogue Input 2 #define A_3 PC_3 // Analogue Input 3 #define A_4 PC_4 // Analogue Input 4 #define A_5 PC_5 // Analogue Input 5 #define D_0 PA_3 // digital output D0 #define D_1 PA_2 // digital output D1 #define D_2 PA_0 // digital output D2 #define D_3 PA_1 // digital output D3 #define D_4 PB_5 // digital output D4 #define D_5 PB_6 // digital output D5 #define D_6 PA_8 // digital output D6 #define D_7 PA_9 // digital output D7 #define D_8 PA_10 // digital output D8 #define D_9 PB_7 // digital output D9 #define D_10 PA_4 // digital output D10 - SPI1 SS #define D_11 PA_4 // digital output D11 - SPI1 MOSI #define D_12 PA_4 // digital output D12 - SPI1 MISO //#define D_13 PA_4 // digital output D13 - SPI1 CLK - GREEN LED #define D_14 PB_8 // digital output D14 #define D_35 PC_6 // pin 13 on Extension #define D_36 PC_7 // pin 14 on Extension #define D_37 PC_8 // pin 15 on Extension (last) // ================= *************** ================== #define USART3_TX PC_10 // D26 - pin 4 on Extension // ================= *************** ================== // serial output? for uLCD // ================= *************** ================== // sensor inputs #include "DS1820.h" #define MAX_PROBES 4 #define ONEWIRE_PIN PB_11 // D30 pin 6 on UEXT // pin 8 on Extension DS1820* probe[MAX_PROBES]; #include "DHT.h" #define DHT_PIN PB_10 // D29 pin 5 on UEXT // pin 7 on Extension // ================= *************** ================== #define NODE_NAME "controller03" // TODO just define node number #define NUM_OUTPUTS 8 DigitalOut outputs[NUM_OUTPUTS] = {D_0, D_1, D_2, D_3, D_4, D_5, D_6, D_7}; #define NUM_INPUTS 6 DigitalIn inputs[NUM_INPUTS] = {PC_0, PC_1, PC_2, PC_3, PC_4, PC_5}; bool input_state[NUM_INPUTS]; Serial pc(USART3_TX, NC); // serial debug output on D26 (pin 4 of Extension) //Serial pc(PA_9, NC); // serial debug output on D7 //Serial xxxxxx // find a serial port for Amp/uLCD connection DigitalIn button(BUTTON); DigitalOut led(LED_GREEN); DHT dht0(DHT_PIN, DHT22); float temp[1]; float humidity[1]; Watchdog wd; Ticker tick_30sec; Ticker tick_5sec; Ticker tick_1sec; Ticker tick_500ms; bool flag_publish; bool flag_read_dht; bool flag_read_ds18b20; typedef MQTT::Client<MQTTSocket,Countdown> MClient; const char* ONOFF[] = {"ON", "OFF"}; const char* OPENCLOSED[] = {"CLOSED", "OPEN"}; enum IO_STATE{IO_ON, IO_OFF}; uint8_t mac_addr[6]={0x00, 0x00, 0x00, 0xBE, 0xEF, 0x03}; // TODO make last byte dynamic const char* mqtt_broker = "192.168.10.4"; //const char* mqtt_broker = "192.168.1.99"; const int mqtt_port = 1883; unsigned long uptime_sec = 0; int connected = -1; void on_control_cmd(const char* topic, const char* message) { int new_state = 0; pc.printf("Received CMD %s %s\r\n", topic, message); // find out command first if(strcmp(message, "ON") == 0) { pc.printf("ON value requested!\r\n"); new_state = IO_ON; } else if(strcmp(message, "OFF") == 0) { pc.printf("OFF value requested!\r\n"); new_state = IO_OFF; } else { pc.printf("Unknown command value specified!\r\n"); // TODO return current value on no message return; } // are we updating an output? if(strncmp(topic, "output", 6) == 0) { // find out which output to apply it to int output_num = int(topic[6])-48; if(output_num >= NUM_OUTPUTS) { pc.printf("ERROR: unknown output num %d\r\n", output_num); } else { // turn something on/off! pc.printf("Output: %d updated to %s\r\n", output_num, ONOFF[new_state]); outputs[output_num] = new_state; flag_publish = 1; // workaround for below // publish_value(client, topic, ONOFF[new_state], false); // needs to access client :-/ } } else { pc.printf("ERROR: Couldn't parse topic: %s\r\n", topic); } } int publish(MClient& client, const char* msg_type, const char* point, const char* payload = NULL, size_t payload_len = 0, bool retain = false, MQTT::QoS qos = MQTT::QOS1){ char topic[64]; sprintf(topic, "%s/" NODE_NAME "/%s", msg_type, point); int ret = client.publish(topic, (void*)payload, payload_len, qos, retain); if(ret == -1) { pc.printf("ERROR during client.publish() = %d\r\n",ret); } return ret; } void messageArrived(MQTT::MessageData& md) { // MQTT callback function MQTT::Message &message = md.message; // copy message payload into local char array IMPROVE ME! char* payload = new char[message.payloadlen+1]; if(!payload) // will this ever happen? return; memcpy(payload, message.payload, message.payloadlen); payload[message.payloadlen]='\0'; // copy topic payload into local char array IMPROVE ME! char* topic = new char[md.topicName.lenstring.len+1]; if(!topic){ // will this ever happen? delete[] payload; return; } memcpy(topic, md.topicName.lenstring.data, md.topicName.lenstring.len); topic[md.topicName.lenstring.len]='\0'; pc.printf("Rcvd: %s : %s\r\n", topic, payload); // find first delimiter in topic string char *topics = strtok (topic,"/"); for (int tok=0; tok<2 && topics != NULL; tok++) // WARNING! hard coded 2 layer topic! { // pc.printf ("Topics %d: %s\r\n",tok, topics); topics = strtok (NULL, "/"); } on_control_cmd(topics, payload); delete[] topic; delete[] payload; } int publish_value(MClient &client, const char *topic, const char *buf, bool retain = false) { return publish(client, "stat", topic, buf, strlen(buf), retain); } void publish_outputs(MClient &client) { for(int i=0; i<NUM_OUTPUTS; i++) { bool output_state = outputs[i]; char topic[] = "outputx"; topic[6] = i+48; pc.printf("Output: %s is %s\r\n", topic, ONOFF[output_state]); connected = publish_value(client, topic, ONOFF[output_state], false); } } void publish_inputs(MClient &client) { for(int i=0; i<NUM_INPUTS; i++) { char topic_str[8]; // long enough string for inputx sprintf(topic_str, "input%d", i); publish_value(client,topic_str,OPENCLOSED[input_state[i]], false); } } void publish_info(MClient &client) { // uptime pc.printf("Uptime %d\r\n", uptime_sec); char uptime_sec_str[12]; // long enough string for a long int sprintf(uptime_sec_str, "%d", uptime_sec); publish_value(client,"uptime",uptime_sec_str, false); // alive publish_value(client, "alive","ON", false); } void read_inputs(MClient &client) { for(int i=0; i<NUM_INPUTS; i++) { bool old_state = input_state[i]; // save old state input_state[i] = inputs[i]; // read new value // pc.printf("Input %d is %d\r\n", i, input_state[i]); if(input_state[i] != old_state) { // input has changed state pc.printf("Input %d changed to %s\r\n", i, OPENCLOSED[input_state[i]]); char topic_str[8]; // long enough string for inputx sprintf(topic_str, "input%d", i); publish_value(client,topic_str,OPENCLOSED[input_state[i]], false); } } } void read_dht(MClient &client) { int error = dht0.readData(); if (0 == error) { temp[0] = dht0.ReadTemperature(CELCIUS); humidity[0] = dht0.ReadHumidity(); pc.printf("Temperature: %3.1f, Humidity: %3.1f\n", temp[0], humidity[0]); } else { pc.printf("DHT read error: %d\n", error); return; } // convert to string and publish char temp_str[6]; sprintf(temp_str, "%3.1f", temp[0]); publish_value(client,"temp0",temp_str, false); char humidity_str[6]; sprintf(humidity_str, "%3.1f", humidity[0]); publish_value(client,"humidity0",humidity_str, false); } void read_ds18b20(MClient &client, int num_ds18b20) { // Announce num of DS18B20 found char temp_str[6]; char topic_str[6]; sprintf(temp_str, "%d", num_ds18b20); publish_value(client,"num_ds18b20",temp_str, false); if(num_ds18b20 > 0) { //Start temperature conversion, wait until ready probe[0]->convertTemperature(true, DS1820::all_devices); for (int i = 0; i<num_ds18b20; i++) { float temp = probe[i]->temperature(); pc.printf("Device %d returns %3.3foC\r\n", i, temp); // convert to string and publish sprintf(temp_str, "%3.3f", temp); sprintf(topic_str, "probetemp%d", i); publish_value(client,topic_str,temp_str, false); } } } int networking_init(MQTTSocket &sock, MClient &client, WIZnetInterface &wiz) { int ret = 0; pc.printf("\n\nNode: %s\r\n", NODE_NAME); pc.printf("%s attempting ethernet connection...\r\n", NODE_NAME); wiz.init(mac_addr); // resets the w5500 if (wiz.connect() == (-1)) { pc.printf("Error getting DHCP address!!\r\n"); } pc.printf("IP: %s\r\n", wiz.getIPAddress()); ret = sock.connect((char*)mqtt_broker,mqtt_port); if(ret != 0){ pc.printf("failed to connect to TCP server\r\n"); return 1; } pc.printf("sock.connect()=%d\r\n",ret); if(client.connect() != 0){ pc.printf("MQTT connect failed\r\n"); return -1; } pc.printf("client.connect()=%d\r\n",ret); ret = client.subscribe("cmnd/" NODE_NAME "/+", MQTT::QOS1, messageArrived); pc.printf("client.subscribe()=%d\r\n", ret); // TODO add client ID when subscribing // Node online message publish_value(client, "alive","ON", false); publish_value(client, "version", VERSION, true); publish_value(client, "IPAddress", wiz.getIPAddress(), true); pc.printf("Initialization done.\r\n"); return 0; } void every_30sec() { // no waits or blocking routines here please! flag_read_dht = 1; flag_read_ds18b20 = 1; } void every_5sec() { // no waits or blocking routines here please! flag_publish = 1; } void every_second() { // no waits or blocking routines here please! uptime_sec++; if(connected == 0) { led = !led; } wd.Service(); // kick the dog before the timeout } void every_500ms() { // no waits or blocking routines here please! if(connected != 0) { led = !led; } } int main() { wd.Configure(20.0); // WIZnetInterface wiz(PA_7, PA_6, PA_5, PA_4, NC); // SPI1 with no reset WIZnetInterface wiz(PB_15, PB_14, PB_13, PB_12, PC_6); // SPI2 with D35 reset MQTTSocket sock; MClient client(sock); tick_500ms.attach(&every_500ms, 0.5); tick_1sec.attach(&every_second, 1.0); tick_5sec.attach(&every_5sec, 5.1); tick_30sec.attach(&every_30sec, 29.5); //pulse all outputs for(int i=0; i<NUM_OUTPUTS; i++) { outputs[i] = IO_OFF; wait(0.2); } // pull high all inputs for(int i=0; i<NUM_INPUTS; i++) { inputs[i].mode(PullUp); } pc.printf("\n\nNode: %s\r\n", NODE_NAME); wd.Service(); // kick the dog before the timeout connected = networking_init(sock, client, wiz); // Initialize DS18B20 probe array to DS1820 objects int num_ds18b20 = 0; while(DS1820::unassignedProbe(ONEWIRE_PIN)) { probe[num_ds18b20] = new DS1820(ONEWIRE_PIN); num_ds18b20++; if (num_ds18b20 == MAX_PROBES) break; } pc.printf("DS18B20: Found %d device(s)\r\n", num_ds18b20); while(1) { read_inputs(client); if(connected != 0) { pc.printf("Restarting network....\r\n"); connected = networking_init(sock, client, wiz); } else { // we're connected, do stuff! if(flag_publish) { publish_outputs(client); publish_inputs(client); publish_info(client); flag_publish = 0; } else if(flag_read_dht) { read_dht(client); flag_read_dht = 0; } else if(flag_read_ds18b20) { read_ds18b20(client, num_ds18b20); flag_read_ds18b20 = 0; } } client.yield(50); // pause a while, yawn...... } }