diff --git a/CmakeLists.txt b/CmakeLists.txt index 5c9da52..2b56486 100644 --- a/CmakeLists.txt +++ b/CmakeLists.txt @@ -44,7 +44,7 @@ endif() # the executable -add_executable(node1 node1.c bme280.c pms5003.c mqtt_client.c) +add_executable(node1 node1.c bme280.c pms5003.c) pico_set_program_version(node1 "0.1") pico_set_program_name(node1 "node_one") @@ -67,20 +67,25 @@ target_compile_definitions(node1 PRIVATE # create map/bin/hex file etc. pico_add_extra_outputs( node1 ) -# == WIFI SCAN == +# == TCP TEST == -add_executable(wifi_scan wifi_scan.c) -target_include_directories(wifi_scan PRIVATE +add_executable(tcp_test tcp.c) +target_include_directories(tcp_test PRIVATE ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts ) -target_link_libraries(wifi_scan +target_link_libraries(tcp_test pico_cyw43_arch_lwip_threadsafe_background pico_stdlib ) -pico_add_extra_outputs(wifi_scan) +target_compile_definitions(tcp_test PRIVATE + WIFI_SSID=\"${WIFI_SSID}\" + WIFI_PASSWORD=\"${WIFI_PASSWORD}\" + ) + +pico_add_extra_outputs(tcp_test) diff --git a/event_proxy/main.go b/event_proxy/main.go index eeca53b..6eb77df 100644 --- a/event_proxy/main.go +++ b/event_proxy/main.go @@ -2,51 +2,60 @@ package main import ( "fmt" - "log" "net" - "os" - "time" -) - -const ( - HOST = "0.0.0.0" - PORT = "8080" - TYPE = "tcp" ) func main() { - listen, err := net.Listen(TYPE, HOST+":"+PORT) - println("Listening on", HOST+":"+PORT) + addr := "192.168.1.153:8080" + fmt.Printf("Starting TCP server on port %s\n", addr) + //Address to bind to + //Resolve address + addr_, err := net.ResolveTCPAddr("tcp", addr) if err != nil { - log.Fatal(err) - os.Exit(1) + fmt.Printf("Error resolving address: %s", err.Error()) + return } - // close listener - defer listen.Close() + //Listen for incoming connections + listener, err := net.ListenTCP("tcp", addr_) + if err != nil { + fmt.Printf("Error starting server: %s", err.Error()) + return + } + defer listener.Close() + for { - conn, err := listen.Accept() + //Accept incoming connection + conn, err := listener.AcceptTCP() if err != nil { - log.Fatal(err) - os.Exit(1) + fmt.Printf("Error accepting connection: %s", err.Error()) + continue } - go handleRequest(conn) + go handleConnection(conn) } } -func handleRequest(conn net.Conn) { - // incoming request +func handleConnection(conn *net.TCPConn) { + defer conn.Close() + fmt.Printf("New connection from %s\n", conn.RemoteAddr().String()) + buffer := make([]byte, 1024) - _, err := conn.Read(buffer) - if err != nil { - log.Fatal(err) + for { + //Read up to 1024 bytes + n, err := conn.Read(buffer) + if err != nil { + fmt.Printf("Error reading from connection: %s", err.Error()) + return + } + if n == 0 { + return + } + fmt.Printf("Received message: %s\n", string(buffer[:n])) + //Echo message back + // _, err = conn.Write(buffer[:n]) + _, err = conn.Write([]byte("1")) + if err != nil { + fmt.Printf("Error writing to connection: %s", err.Error()) + return + } } - - // write data to response - time := time.Now().Format(time.ANSIC) - fmt.Printf("Message recived: %v. Received time: %v", string(buffer[:]), time) - conn.Write([]byte("1")) - // conn.Write([]byte(responseStr)) - - // close conn - conn.Close() } diff --git a/node1.c b/node1.c index b48d814..b419574 100644 --- a/node1.c +++ b/node1.c @@ -1,7 +1,6 @@ #include "bme280.h" #include "pico/stdlib.h" #include "pms5003.h" -#include "mqtt_client.h" #include #include #include @@ -56,8 +55,6 @@ static bool cb_24h(__unused struct repeating_timer *t) { return false; // Not reached } -static mqtt_client_config mqtt_config; - static pms5003_config pms_config; static pms5003_reading current_pms5003_reading; @@ -101,12 +98,13 @@ static bool cb_30(__unused struct repeating_timer *t) { printf("PM2.5: %.2f\n", current_pms5003_reading.pm2_5); printf("PM10: %.2f\n", current_pms5003_reading.pm10); // char msg[100]; - char msg[200]; - snprintf(msg, sizeof(msg), "{\"temp\": %.2f, \"pressure\": %.2f, \"humidity\": %.2f, \"pm1\": %.2f, \"pm2_5\": %.2f, \"pm10\": %.2f}\n", - current_bem280_reading.temperature, current_bem280_reading.pressure, current_bem280_reading.humidity, - current_pms5003_reading.pm1, current_pms5003_reading.pm2_5, current_pms5003_reading.pm10); + // char msg[200]; + // snprintf(msg, sizeof(msg), "{\"temp\": %.2f, \"pressure\": %.2f, \"humidity\": %.2f, \"pm1\": %.2f, \"pm2_5\": %.2f, \"pm10\": %.2f}\n", + // current_bem280_reading.temperature, current_bem280_reading.pressure, current_bem280_reading.humidity, + // current_pms5003_reading.pm1, current_pms5003_reading.pm2_5, current_pms5003_reading.pm10); printf("Sending data to home assistant...\n"); - mqtt_client_pub_message(&mqtt_config, msg); + // printf + // mqtt_client_pub_message(&mqtt_config, msg); return true; } @@ -141,7 +139,6 @@ int main() { // Initialize communication LED comms_led_init(); - mqtt_client_init(&mqtt_config, "homeassistant/sensor/bws/node1/state", "bws-node1"); // Setup BME280 bme280_init(&bem_config, i2c1, 14, 15); @@ -155,9 +152,7 @@ int main() { add_repeating_timer_ms(86400000, cb_24h, NULL, &timer_24h); while (true) { comms_led_update(); - sleep_us(100); tight_loop_contents(); - mqtt_client_do_network_stuff(&mqtt_config); watchdog_update(); } } diff --git a/tcp.c b/tcp.c new file mode 100644 index 0000000..b67daa1 --- /dev/null +++ b/tcp.c @@ -0,0 +1,253 @@ +/** + * Copyright (c) 2022 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include "pico/stdlib.h" +#include "pico/cyw43_arch.h" + +#include "lwip/pbuf.h" +#include "lwip/tcp.h" + +#define TEST_TCP_SERVER_IP "192.168.1.153" + +#define TCP_PORT 8080 +#define BUF_SIZE 2048 + +#define TEST_ITERATIONS 10 +#define POLL_TIME_S 5 + +#if 0 +static void dump_bytes(const uint8_t *bptr, uint32_t len) { + unsigned int i = 0; + + printf("dump_bytes %d", len); + for (i = 0; i < len;) { + if ((i & 0x0f) == 0) { + printf("\n"); + } else if ((i & 0x07) == 0) { + printf(" "); + } + printf("%02x ", bptr[i++]); + } + printf("\n"); +} +#define DUMP_BYTES dump_bytes +#else +#define DUMP_BYTES(A,B) +#endif + +typedef struct TCP_CLIENT_T_ { + struct tcp_pcb *tcp_pcb; + ip_addr_t remote_addr; + uint8_t buffer[BUF_SIZE]; + int buffer_len; + int sent_len; + bool complete; + int run_count; + bool connected; +} TCP_CLIENT_T; + +static err_t tcp_client_close(void *arg) { + TCP_CLIENT_T *state = (TCP_CLIENT_T*)arg; + err_t err = ERR_OK; + if (state->tcp_pcb != NULL) { + tcp_arg(state->tcp_pcb, NULL); + tcp_poll(state->tcp_pcb, NULL, 0); + tcp_sent(state->tcp_pcb, NULL); + tcp_recv(state->tcp_pcb, NULL); + tcp_err(state->tcp_pcb, NULL); + err = tcp_close(state->tcp_pcb); + if (err != ERR_OK) { + printf("close failed %d, calling abort\n", err); + tcp_abort(state->tcp_pcb); + err = ERR_ABRT; + } + state->tcp_pcb = NULL; + } + return err; +} + +// Called with results of operation +static err_t tcp_result(void *arg, int status) { + TCP_CLIENT_T *state = (TCP_CLIENT_T*)arg; + if (status == 0) { + printf("test success\n"); + } else { + printf("test failed %d\n", status); + } + state->complete = true; + return tcp_client_close(arg); +} + +static err_t tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len) { + TCP_CLIENT_T *state = (TCP_CLIENT_T*)arg; + printf("tcp_client_sent %u\n", len); + state->sent_len += len; + + if (state->sent_len >= BUF_SIZE) { + + state->run_count++; + if (state->run_count >= TEST_ITERATIONS) { + tcp_result(arg, 0); + return ERR_OK; + } + + // We should receive a new buffer from the server + state->buffer_len = 0; + state->sent_len = 0; + printf("Waiting for buffer from server\n"); + } + + return ERR_OK; +} + +static err_t tcp_client_connected(void *arg, struct tcp_pcb *tpcb, err_t err) { + TCP_CLIENT_T *state = (TCP_CLIENT_T*)arg; + if (err != ERR_OK) { + printf("connect failed %d\n", err); + return tcp_result(arg, err); + } + state->connected = true; + printf("Waiting for buffer from server\n"); + return ERR_OK; +} + +static err_t tcp_client_poll(void *arg, struct tcp_pcb *tpcb) { + printf("tcp_client_poll\n"); + return tcp_result(arg, -1); // no response is an error? +} + +static void tcp_client_err(void *arg, err_t err) { + if (err != ERR_ABRT) { + printf("tcp_client_err %d\n", err); + tcp_result(arg, err); + } +} + +err_t tcp_client_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) { + TCP_CLIENT_T *state = (TCP_CLIENT_T*)arg; + if (!p) { + return tcp_result(arg, -1); + } + // this method is callback from lwIP, so cyw43_arch_lwip_begin is not required, however you + // can use this method to cause an assertion in debug mode, if this method is called when + // cyw43_arch_lwip_begin IS needed + cyw43_arch_lwip_check(); + if (p->tot_len > 0) { + printf("recv %d err %d\n", p->tot_len, err); + for (struct pbuf *q = p; q != NULL; q = q->next) { + DUMP_BYTES(q->payload, q->len); + } + // Receive the buffer + const uint16_t buffer_left = BUF_SIZE - state->buffer_len; + state->buffer_len += pbuf_copy_partial(p, state->buffer + state->buffer_len, + p->tot_len > buffer_left ? buffer_left : p->tot_len, 0); + tcp_recved(tpcb, p->tot_len); + } + pbuf_free(p); + + // If we have received the whole buffer, send it back to the server + if (state->buffer_len == BUF_SIZE) { + printf("Writing %d bytes to server\n", state->buffer_len); + err_t err = tcp_write(tpcb, state->buffer, state->buffer_len, TCP_WRITE_FLAG_COPY); + if (err != ERR_OK) { + printf("Failed to write data %d\n", err); + return tcp_result(arg, -1); + } + } + return ERR_OK; +} + +static bool tcp_client_open(void *arg) { + TCP_CLIENT_T *state = (TCP_CLIENT_T*)arg; + printf("Connecting to %s port %u\n", ip4addr_ntoa(&state->remote_addr), TCP_PORT); + state->tcp_pcb = tcp_new_ip_type(IP_GET_TYPE(&state->remote_addr)); + if (!state->tcp_pcb) { + printf("failed to create pcb\n"); + return false; + } + + tcp_arg(state->tcp_pcb, state); + tcp_poll(state->tcp_pcb, tcp_client_poll, POLL_TIME_S * 2); + tcp_sent(state->tcp_pcb, tcp_client_sent); + tcp_recv(state->tcp_pcb, tcp_client_recv); + tcp_err(state->tcp_pcb, tcp_client_err); + + state->buffer_len = 0; + + // cyw43_arch_lwip_begin/end should be used around calls into lwIP to ensure correct locking. + // You can omit them if you are in a callback from lwIP. Note that when using pico_cyw_arch_poll + // these calls are a no-op and can be omitted, but it is a good practice to use them in + // case you switch the cyw43_arch type later. + cyw43_arch_lwip_begin(); + err_t err = tcp_connect(state->tcp_pcb, &state->remote_addr, TCP_PORT, tcp_client_connected); + cyw43_arch_lwip_end(); + + return err == ERR_OK; +} + +// Perform initialisation +static TCP_CLIENT_T* tcp_client_init(void) { + TCP_CLIENT_T *state = calloc(1, sizeof(TCP_CLIENT_T)); + if (!state) { + printf("failed to allocate state\n"); + return NULL; + } + ip4addr_aton(TEST_TCP_SERVER_IP, &state->remote_addr); + return state; +} + +void run_tcp_client_test(void) { + TCP_CLIENT_T *state = tcp_client_init(); + if (!state) { + return; + } + if (!tcp_client_open(state)) { + tcp_result(state, -1); + return; + } + while(!state->complete) { + // the following #ifdef is only here so this same example can be used in multiple modes; + // you do not need it in your code +#if PICO_CYW43_ARCH_POLL + // if you are using pico_cyw43_arch_poll, then you must poll periodically from your + // main loop (not from a timer) to check for Wi-Fi driver or lwIP work that needs to be done. + cyw43_arch_poll(); + // you can poll as often as you like, however if you have nothing else to do you can + // choose to sleep until either a specified time, or cyw43_arch_poll() has work to do: + cyw43_arch_wait_for_work_until(make_timeout_time_ms(1000)); +#else + // if you are not using pico_cyw43_arch_poll, then WiFI driver and lwIP work + // is done via interrupt in the background. This sleep is just an example of some (blocking) + // work you might be doing. + sleep_ms(1000); +#endif + } + free(state); +} + +int main() { + stdio_init_all(); + + if (cyw43_arch_init()) { + printf("failed to initialise\n"); + return 1; + } + cyw43_arch_enable_sta_mode(); + + printf("Connecting to Wi-Fi...\n"); + if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000)) { + printf("failed to connect.\n"); + return 1; + } else { + printf("Connected.\n"); + } + run_tcp_client_test(); + cyw43_arch_deinit(); + return 0; +}