W tym poście chciałbym opisać sposób sterowania elementami układu przez stronę internetową.
[Źródło: http://www.st.com/en/evaluation-tools/32f746gdiscovery.html]
Opis programu:
Program ma za zadanie sterowanie urządzeniem przez stronę internetową. W przykładzie zaimplementowałem takie funkcje jak:
- odczyt stanu przekaźników/pinów wraz z możliwością sterowania;
- odczyt danych z czujników pomiarowych;
- odczytanie czasu z urządzenia;
- zmiana koloru tła na ekranie;
- zmiana napisu na wyświetlaczu;
- graficzna prezentacja temperatury;
Na samym początku musimy uruchomić zadanie odpowiedzialne za ustanowienie połączenia i stworzenie serwera HTTP pod zadanym adresem:
- void StartDefaultTask(void const * argument)
- {
- MX_LWIP_Init();
- /* USER CODE BEGIN 5 */
- struct netconn *netConn;
- err_t err;
- netConn = netconn_new(NETCONN_TCP);
- if(netConn != NULL)
- {
- socket1.conn = netConn;
- err = netconn_bind(netConn, NULL, 80);
- if (err == ERR_OK)
- {
- netconn_listen(netConn);
- sys_thread_new("tcp_thread1", tcp_thread, (void*)&socket1, DEFAULT_THREAD_STACKSIZE, osPriorityNormal );
- }
- else
- {
- netconn_delete(netConn);
- }
- }
- /* Infinite loop */
- for(;;) { osDelay(1); }
- /* USER CODE END 5 */
- }
Drugie zadanie pobiera informację od strony internetowej i dodaje je do kolejki:
- void stringOutputTaksAddMailQue(void const * argument)
- {
- osEvent event;
- for(;;)
- {
- event = osMailGet(strout_Queue, osWaitForever);
- if (event.status == osEventMail)
- {
- struct_out *queueStruct;
- queueStruct = event.value.p;
- }
- }
- }
Poniżej przejdę przez funkcję wywoływane przez stronę internetową:
Na samym początku zmiana koloru tła. Nowy kolor jest wybierany na podstawie przekazanego numeru:
- void setScreenBackgroundColor(uint8_t selectedColor)
- {
- switch (selectedColor)
- {
- case '1':
- ROCKTECH_FillScreen(LCD_COLOR_RED);
- break;
- case '2':
- ROCKTECH_FillScreen(LCD_COLOR_GREEN);
- break;
- case '3':
- ROCKTECH_FillScreen(LCD_COLOR_BLUE);
- break;
- case '4':
- ROCKTECH_FillScreen(LCD_COLOR_BLACK);
- break;
- case '5':
- ROCKTECH_FillScreen(LCD_COLOR_CYAN);
- break;
- case '6':
- ROCKTECH_FillScreen(LCD_COLOR_MAGENTA);
- break;
- case '7':
- ROCKTECH_FillScreen(LCD_COLOR_YELLOW);
- break;
- case '8':
- ROCKTECH_FillScreen(LCD_COLOR_WHITE);
- break;
- default:
- ROCKTECH_FillScreen(LCD_COLOR_WHITE);
- break;
- }
- }
Kolejna funkcja odpowiada za sterowanie przekaźnikami:
- static uint8_t changeStateOfRelay(uint8_t relayNumber)
- {
- switch (relayNumber)
- {
- case '1':
- return toggleAndCheckRelayState_1();
- break;
- case '2':
- return toggleAndCheckRelayState_2();
- break;
- case '3':
- return toggleAndCheckRelayState_3();
- break;
- case '4':
- return toggleAndCheckRelayState_4();
- break;
- case '5':
- return toggleAndCheckRelayState_5();
- break;
- default:
- return 0;
- break;
- }
- }
Po odebraniu komendy o zmianie stanu przekaźnika ustawiamy nowy stan na przeciwny. Następnie odczytujemy aktualny stan przekaźnika, na tej podstawie zwracamy wartość, która określi informację o przesłaniu danych do serwera.
- static uint8_t toggleAndCheckRelayState_1()
- {
- HAL_GPIO_TogglePin(RELAY_1_PORT, RELAY_1_PIN);
- return checkRelayState_1();
- }
- void prepareResponseString(uint8_t *responseFramePtr, uint8_t selectedRelay, uint8_t relayState)
- {
- sprintf((char *)responseFramePtr, "relay.html?r=x_Ox");
- responseFramePtr[13] = selectedRelay;
- if(relayState == 1)
- {
- responseFramePtr[16] = 'n';
- }
- else if(relayState == 2)
- {
- responseFramePtr[16] = 'f';
- }
- }
Funkcja ustawiająca przekaźniki na stronie serwera HTTP:
- function GetRelayState(val){
- nocache = "&nocache=" + Math.random() * 1000000;
- var request = new XMLHttpRequest();
- request.onreadystatechange = function(){
- if(this.readyState == 4){
- if(this.status == 200){
- if(this.responseText != null){
- var msg = this.responseText;
- if(val == 1)
- {
- if(msg.includes("relay.html?r=1_On") == true)
- {
- document.getElementById("controlRelayBtn1").innerHTML = '<input class = "controlRelayBtn" type="button" style="color: #000000; background-color: #00ff00;" onclick="GetRelayState()" value="Przekaźnik 1 ON"/>';
- }
- else
- {
- document.getElementById("controlRelayBtn1").innerHTML = '<input class = "controlRelayBtn" type="button" style="color: #00ffff; background-color: #ff0000;" onclick="GetRelayState()" value="Przekaźnik 1 OFF"/>';
- }
- }
- else if(val == 2)
- {
- if(msg.includes("relay.html?r=2_On") == true)
- {
- document.getElementById("controlRelayBtn2").innerHTML = '<input class = "controlRelayBtn" type="button" style="color: #000000; background-color: #00ff00;" onclick="GetRelayState()" value="Przekaźnik 2 ON"/>';
- }
- else
- {
- document.getElementById("controlRelayBtn2").innerHTML = '<input class = "controlRelayBtn" type="button" style="color: #00ffff; background-color: #ff0000;" onclick="GetRelayState()" value="Przekaźnik 2 OFF"/>';
- }
- }
- else if(val == 3)
- {
- if(msg.includes("relay.html?r=3_On") == true)
- {
- document.getElementById("controlRelayBtn3").innerHTML = '<input class = "controlRelayBtn" type="button" style="color: #000000; background-color: #00ff00;" onclick="GetRelayState()" value="Przekaźnik 3 ON"/>';
- }
- else
- {
- document.getElementById("controlRelayBtn3").innerHTML = '<input class = "controlRelayBtn" type="button" style="color: #00ffff; background-color: #ff0000;" onclick="GetRelayState()" value="Przekaźnik 3 OFF"/>';
- }
- }
- else if(val == 4)
- {
- if(msg.includes("relay.html?r=4_On") == true)
- {
- document.getElementById("controlRelayBtn4").innerHTML = '<input class = "controlRelayBtn" type="button" style="color: #000000; background-color: #00ff00;" onclick="GetRelayState()" value="Przekaźnik 4 ON"/>';
- }
- else
- {
- document.getElementById("controlRelayBtn4").innerHTML = '<input class = "controlRelayBtn" type="button" style="color: #00ffff; background-color: #ff0000;" onclick="GetRelayState()" value="Przekaźnik 4 OFF"/>';
- }
- }
- else if(val == 5)
- {
- if(msg.includes("relay.html?r=5_On") == true)
- {
- document.getElementById("controlRelayBtn5").innerHTML = '<input class = "controlRelayBtn" type="button" style="color: #000000; background-color: #00ff00;" onclick="GetRelayState()" value="Przekaźnik 5 ON"/>';
- }
- else
- {
- document.getElementById("controlRelayBtn5").innerHTML = '<input class = "controlRelayBtn" type="button" style="color: #00ffff; background-color: #ff0000;" onclick="GetRelayState()" value="Przekaźnik 5 OFF"/>';
- }
- }
- }
- }
- }
- }
- if(val == 1)
- {
- request.open("GET", "relay.html?r=1" + nocache, true);
- request.send(null);
- }
- else if(val == 2)
- {
- request.open("GET", "relay.html?r=2" + nocache, true);
- request.send(null);
- }
- else if(val == 3)
- {
- request.open("GET", "relay.html?r=3" + nocache, true);
- request.send(null);
- }
- else if(val == 4)
- {
- request.open("GET", "relay.html?r=4" + nocache, true);
- request.send(null);
- }
- else if(val == 5)
- {
- request.open("GET", "relay.html?r=5" + nocache, true);
- request.send(null);
- }
- }
Dodatkowa funkcja pozwalająca na odczytanie stanu wszystkich przekaźników wygląda następująco:
- void prepareResponseStringWithRelaysState(uint8_t *responseFramePtr)
- {
- sprintf((char *)responseFramePtr, "GET /relay.html?A=1x;2x;3x;4x;5x");
- if(checkRelayState_1() == 1) { *(responseFramePtr + 19) = 'O'; }
- else { *(responseFramePtr + 19) = 'F'; }
- if(checkRelayState_2() == 1) { *(responseFramePtr + 22) = 'O'; }
- else { *(responseFramePtr + 22) = 'F'; }
- if(checkRelayState_3() == 1) { *(responseFramePtr + 25) = 'O'; }
- else { *(responseFramePtr + 25) = 'F'; }
- if(checkRelayState_4() == 1) { *(responseFramePtr + 28) = 'O'; }
- else { *(responseFramePtr + 28) = 'F'; }
- if(checkRelayState_5() == 1) { *(responseFramePtr + 31) = 'O'; }
- else { *(responseFramePtr + 31) = 'F'; }
- }
Funkcja obsługujące wyświetlenie danych ze stanem wszystkich przekaźników na stronie serwera HTTP:
- function GetRelaysStates()
- {
- nocache = "&nocache=" + Math.random() * 1000000;
- var request = new XMLHttpRequest();
- request.onreadystatechange = function(){
- if(this.readyState == 4){
- if(this.status == 200){
- if(this.responseText != null){
- var msg = this.responseText;
- if(msg.indexOf('1O') != -1)
- {
- document.getElementById("controlRelayBtn1").innerHTML = '<input class = "controlRelayBtn" type="button" style="color: #000000; background-color: #00ff00;" onclick="GetRelayState()" value="Przekaźnik 1 ON"/>';
- }
- else
- {
- document.getElementById("controlRelayBtn1").innerHTML = '<input class = "controlRelayBtn" type="button" style="color: #00ffff; background-color: #ff0000;" onclick="GetRelayState()" value="Przekaźnik 1 OFF"/>';
- }
- if(msg.indexOf('2O') != -1)
- {
- document.getElementById("controlRelayBtn2").innerHTML = '<input class = "controlRelayBtn" type="button" style="color: #000000; background-color: #00ff00;" onclick="GetRelayState()" value="Przekaźnik 2 ON"/>';
- }
- else
- {
- document.getElementById("controlRelayBtn2").innerHTML = '<input class = "controlRelayBtn" type="button" style="color: #00ffff; background-color: #ff0000;" onclick="GetRelayState()" value="Przekaźnik 2 OFF"/>';
- }
- if(msg.indexOf('3O') != -1)
- {
- document.getElementById("controlRelayBtn3").innerHTML = '<input class = "controlRelayBtn" type="button" style="color: #000000; background-color: #00ff00;" onclick="GetRelayState()" value="Przekaźnik 3 ON"/>';
- }
- else
- {
- document.getElementById("controlRelayBtn3").innerHTML = '<input class = "controlRelayBtn" type="button" style="color: #00ffff; background-color: #ff0000;" onclick="GetRelayState()" value="Przekaźnik 3 OFF"/>';
- }
- if(msg.indexOf('4O') != -1)
- {
- document.getElementById("controlRelayBtn4").innerHTML = '<input class = "controlRelayBtn" type="button" style="color: #000000; background-color: #00ff00;" onclick="GetRelayState()" value="Przekaźnik 4 ON"/>';
- }
- else
- {
- document.getElementById("controlRelayBtn4").innerHTML = '<input class = "controlRelayBtn" type="button" style="color: #00ffff; background-color: #ff0000;" onclick="GetRelayState()" value="Przekaźnik 4 OFF"/>';
- }
- if(msg.indexOf('5O') != -1)
- {
- document.getElementById("controlRelayBtn5").innerHTML = '<input class = "controlRelayBtn" type="button" style="color: #000000; background-color: #00ff00;" onclick="GetRelayState()" value="Przekaźnik 5 ON"/>';
- }
- else
- {
- document.getElementById("controlRelayBtn5").innerHTML = '<input class = "controlRelayBtn" type="button" style="color: #00ffff; background-color: #ff0000;" onclick="GetRelayState()" value="Przekaźnik 5 OFF"/>';
- }
- }
- }
- }
- }
- request.open("GET", "relay.html?A" + nocache, true);
- request.send(null);
- }
Następnie mamy przesyłanie tekstu jaki ma zostać wyświetlony na ekranie:
Funkcja odpowiedzialna za przesłanie ramki danych do mikrokontrolera:
- function sendDataToDisplay()
- {
- xhr.open("GET", "distxt.html?d=" + document.getElementById("myText").value + '@', true);
- xhr.responseType = "text";
- xhr.send(null);
- }
Funkcja odpowiedzialna za wypisanie tekstu na ekranie:
Odczytywanie parametrów takich jak czas, temperatura, ciśnienie, wilgotność, wysokość. Poniżej przykładowa funkcja odczytująca temperaturę. Wiadomość wyświetlana jest w polu tekstowym na stronie HTTP:
Zdekodowanie oraz przesłanie odpowiedzi na stronę:
- void displayTextOnScreen(uint8_t *sendFramePtr)
- {
- uint8_t bufferWithData[100] = {0x00};
- for(uint8_t i = 0, j = 0; i<100; i++, j++)
- {
- if(*(sendFramePtr + 19 + j) == '%' && *(sendFramePtr + 19 + j + 1) == '2' && *(sendFramePtr + 19 + j + 2) == '0')
- {
- bufferWithData[i] = ' ';
- j += 2;
- }
- else if(*(sendFramePtr + 19 + j) != '@')
- {
- bufferWithData[i] = *(sendFramePtr + 19 + j);
- }
- else
- {
- bufferWithData[i] = '\n';
- break;
- }
- }
- ROCKTECH_DisplayString(0, 40, (uint8_t *)bufferWithData, CENTER_MODE, 0);
- }
Odczytywanie parametrów takich jak czas, temperatura, ciśnienie, wilgotność, wysokość. Poniżej przykładowa funkcja odczytująca temperaturę. Wiadomość wyświetlana jest w polu tekstowym na stronie HTTP:
- function loadTimeFromDevice(){
- nocache = "&nocache=" + Math.random() * 1000000;
- var request = new XMLHttpRequest();
- request.onreadystatechange = function(){
- if(this.readyState == 4){
- if(this.status == 200){
- if(this.responseText != null){
- var msg = this.responseText;
- var msgToDisplay = msg.substring(17, 33);
- document.getElementById("readedTimeLbl").innerHTML = "Czas: " + msgToDisplay;
- }
- }
- }
- }
- request.open("GET", "time.html?T=" + nocache, true);
- request.send(null);
- }
Zdekodowanie oraz przesłanie odpowiedzi na stronę:
- else if (strncmp((char const *)buf,"GET /time.html?T=",17)==0)
- {
- uint8_t responseFrame[36] = {0x00};
- prepareResponseStringWithTimeExampleFunct(&responseFrame[0]);
- netconn_write(newconn, (const unsigned char*)responseFrame, (size_t)35, NETCONN_NOCOPY);
- }
Za odczytanie wszystkich parametrów z czujników odpowiada następująca funkcja przesyłająca oraz dekodująca dane od strony serwera HTTP:
- function loadAllDataFromDevice(){
- nocache = "&nocache=" + Math.random() * 1000000;
- var request = new XMLHttpRequest();
- request.onreadystatechange = function(){
- if(this.readyState == 4){
- if(this.status == 200){
- if(this.responseText != null){
- var msg = this.responseText;
- var msgToDisplay = msg.substring(21, 37);
- document.getElementById("readedTimeLbl").innerHTML = "Czas: " + msgToDisplay;
- msgToDisplay = msg.substring(38, 42);
- document.getElementById("readedTempLbl").innerHTML = "Temperatura: " + msgToDisplay;
- msgToDisplay = msg.substring(44, 46);
- document.getElementById("readedHumidLbl").innerHTML = "Wilgotność: " + msgToDisplay + "%";
- msgToDisplay = msg.substring(47, 53);
- document.getElementById("readedPressureLbl").innerHTML = "Ciśnienie: " + msgToDisplay + "hPa";
- msgToDisplay = msg.substring(54, 59);
- document.getElementById("readedAltitudeLbl").innerHTML = "Wysokość: " + msgToDisplay + "mnpm";
- }
- }
- }
- }
- request.open("GET", "alldata.html?AD=" + nocache, true);
- request.send(null);
- }