Coordinator v2
Dependencies: NerfUSXbee PinDetect EthernetInterface JSON MFRC522 WebSocketClient mbed-rtos mbed
Revision 1:e1c5259b7d9a, committed 2017-04-10
- Comitter:
- Ismael Balafrej
- Date:
- Mon Apr 10 15:02:24 2017 -0400
- Child:
- 2:019d8848cf7e
- Commit message:
- V1
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Mon Apr 10 15:02:24 2017 -0400 @@ -0,0 +1,10 @@ +BUILD +.temp +JSON +mbed-rtos +mbed +WebSocketClient +.git +MFRC522 +JSON +EthernetInterface
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NerfUSXbee.lib Mon Apr 10 15:02:24 2017 -0400 @@ -0,0 +1,1 @@ +https://mbed.org/teams/NerfUS/code/NerfUSXbee/ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PinDetect.lib Mon Apr 10 15:02:24 2017 -0400 @@ -0,0 +1,1 @@ +https://mbed.org/users/AjK/code/PinDetect/#cb3afc45028b
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/BtnHandler.hpp Mon Apr 10 15:02:24 2017 -0400 @@ -0,0 +1,24 @@ +#pragma once +#include "ports.hpp" +#include "PinDetect.h" +#include "ServerData.hpp" +#include "serverEvents.hpp" + +class BtnHandler { + PinDetect _interrupt; + ServerEvent event; + public: + BtnHandler(PinName pin, const char *eventName) : _interrupt(pin) { + strcpy(event.event, eventName); + _interrupt.mode( PullDown ); + _interrupt.setSampleFrequency(); + _interrupt.attach_asserted(this, &BtnHandler::sendEvent); + } + BtnHandler(PinName pin, const char *eventName, const char *direction) : BtnHandler(pin, eventName) { + strcpy(event.data.direction, direction); + } + void sendEvent() + { + websocket_message_send(event); + } +}; \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/FreeMem.h Mon Apr 10 15:02:24 2017 -0400 @@ -0,0 +1,36 @@ +/* Using malloc() to determine free memory.*/ +//https://developer.mbed.org/questions/6994/How-to-print-Free-RAM-available-RAM-or-u/ + +#include <stdio.h> +#include <stdlib.h> +#define FREEMEM_CELL 100 +struct elem { /* Definition of a structure that is FREEMEM_CELL bytes in size.) */ + struct elem *next; + char dummy[FREEMEM_CELL-2]; +}; +int FreeMem(void) { + int counter; + struct elem *head, *current, *nextone; + current = head = (struct elem*) malloc(sizeof(struct elem)); + if (head == NULL) + return 0; /*No memory available.*/ + counter = 0; + // __disable_irq(); + do { + counter++; + current->next = (struct elem*) malloc(sizeof(struct elem)); + current = current->next; + } while (current != NULL); + /* Now counter holds the number of type elem + structures we were able to allocate. We + must free them all before returning. */ + current = head; + do { + nextone = current->next; + free(current); + current = nextone; + } while (nextone != NULL); + // __enable_irq(); + + return counter*FREEMEM_CELL; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/GameCoordinator.hpp Mon Apr 10 15:02:24 2017 -0400 @@ -0,0 +1,30 @@ +#pragma once +#include <vector> +#include "ports.hpp" +#include "Target.hpp" +#include "ServerData.hpp" +#include "GameMode.hpp" +#include "ReflexMode.hpp" +#include "SpeedMode.hpp" + +class GameCoordinator +{ + public: + GameCoordinator(); + + void start_game(ServerData *configs); + void stop_game(); + void ticker_callback(); + void get_next_round(); + + vector<Target> targets; + Thread Tget_next_round; + private: + vector<GameMode*> game_modes; + GameMode* current_game; + uint16_t target_timeout = 10000; + uint8_t number_of_targets = 10; + Ticker ticker; +}; + +extern GameCoordinator gameCoordinator; \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/GameMode.hpp Mon Apr 10 15:02:24 2017 -0400 @@ -0,0 +1,42 @@ +#pragma once +#include <vector> +#include "ports.hpp" +#include "Target.hpp" +#include "ServerData.hpp" + +class GameMode +{ +public: + GameMode(vector<Target> *_targets) + { + targets = _targets; + } + + virtual bool IsWeaponValid(int weaponId) + { + return true; //Default to any Weapons + } + + virtual ServerData GetStats() + { + return stats; + } + + virtual Target* GetNextTarget() = 0; + virtual void OnTargetHit(int timeTaken) = 0; + virtual void OnTargetMiss() = 0; + virtual int getTimeBetweenTargets() + { + return 1000; //default to 1 sec + } + +protected: + ServerData stats; + vector<Target>* targets; + Target *GetRandomTarget() + { + int target_nb = rand() % targets->size(); + return &(targets->at(target_nb)); + + } +}; \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/GameModes/ReflexMode.hpp Mon Apr 10 15:02:24 2017 -0400 @@ -0,0 +1,25 @@ +#pragma once +#include "GameMode.hpp" + +class ReflexMode : public GameMode +{ + public: + using GameMode::GameMode; + Target* GetNextTarget() + { + return GetRandomTarget(); + } + void OnTargetHit(int timeTaken) + { + stats.targets++; + stats.score += 5; + } + void OnTargetMiss() + { + stats.score -= 1; + } + int getTimeBetweenTargets() + { + return rand() % 3000 + 500; + } +}; \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/GameModes/SpeedMode.hpp Mon Apr 10 15:02:24 2017 -0400 @@ -0,0 +1,25 @@ +#pragma once +#include "GameMode.hpp" + +class SpeedMode : public GameMode +{ + public: + using GameMode::GameMode; + Target* GetNextTarget() + { + return GetRandomTarget(); + } + void OnTargetHit(int timeTaken) + { + stats.targets++; + stats.score += 5; + } + void OnTargetMiss() + { + stats.score -= 1; + } + int getTimeBetweenTargets() + { + return 1000; + } +}; \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/ServerData.hpp Mon Apr 10 15:02:24 2017 -0400 @@ -0,0 +1,26 @@ +#pragma once + +//TODO: Separate ? +struct ServerData +{ + //To server + uint8_t targets = 0; + uint8_t enemies = 0; + uint8_t allies = 0; + uint16_t averageReflexTime = 0; + uint16_t gameLength = 0; + uint16_t score = 0; + char rfid_code[32] = {'\0'}; + char direction[10] = {'\0'}; + + //From server + uint8_t game_id = 0; + uint16_t max_reflex_time = 10000; + uint8_t number_of_target = 100; + +}; +struct ServerEvent +{ + char event[32] = {'\0'}; + ServerData data; +}; \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/Target.hpp Mon Apr 10 15:02:24 2017 -0400 @@ -0,0 +1,19 @@ +#pragma once +#include <vector> +#include "ports.hpp" +#include "RealXbeeTransmitter.hpp" + +extern RealXbeeTransmitter xbee_transmitter; + +struct Target +{ + int target_number; //0 or 1 + int addr_table[8]; //64 bit addr + + Target(int address_msb, int address_lsb, int target_number); + + //mode: ally (0) or enemy (1), timeout 16 bits in ms + void rise(int mode, int timeout); + + void get_address_for_xbee(int address_msb, int address_lsb); +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/dispatcher.hpp Mon Apr 10 15:02:24 2017 -0400 @@ -0,0 +1,23 @@ +#pragma once +#include "ports.hpp" +#include "ServerData.hpp" +#include "GameCoordinator.hpp" +#include <vector> + +void dispatch_event_from_target(vector<uint8_t> data, int* address) +{ + //addr: table of 8 int + //TODO - calc stats +} + +void dispatch_event_from_server(ServerEvent *event) { + toPc("Event Name: %s", event->event); + if (strcmp(event->event, "start_game") == 0) + { + gameCoordinator.start_game(&(event->data)); + } + else if (strcmp(event->event, "request_report") == 0) + { + gameCoordinator.stop_game(); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/jsonParser.hpp Mon Apr 10 15:02:24 2017 -0400 @@ -0,0 +1,55 @@ +#pragma once +#include "ports.hpp" +#include "Json.h" +#include "ServerData.hpp" + +bool get_value_from_json(char *jsonSource, const char *keyName, char *outValue) +{ + Json json(jsonSource, strlen(jsonSource), 40); //Can add 3rd argument if json is too big, default: 32 tokens + if (!json.isValidJson()) + { + toPc("Invalid JSON: %s", jsonSource); + return false; + } + if (json.type(0) != JSMN_OBJECT) + { + toPc("Invalid JSON. ROOT element is not Object: %s", jsonSource); + return false; + } + int keyIndex = json.findKeyIndexIn(keyName, 0); + if (keyIndex == -1) + { + //toPc("\"%s\" key not found in json: %s", keyName, jsonSource); + return false; + } + int valueIndex = json.findChildIndexOf(keyIndex, -1); + if (valueIndex > 0) + { + const char *valueStart = json.tokenAddress(valueIndex); + int valueLength = json.tokenLength(valueIndex); + strncpy(outValue, valueStart, valueLength); + outValue[valueLength] = 0; + return true; + } + return false; +} + +void parse_server_event_from_json(char *jsonSource, ServerEvent *returnEvent) +{ + char data[250]; //container for the json in "data" + get_value_from_json(jsonSource, "event", returnEvent->event); + + bool data_parse_successful = get_value_from_json(jsonSource, "data", data); + if (data_parse_successful) + { + char tmp[32]; + if (get_value_from_json(data, "game_id", tmp)) + returnEvent->data.game_id = atoi(tmp); + + if (get_value_from_json(data, "max_reflex_time", tmp)) + returnEvent->data.max_reflex_time = atoi(tmp); + + if (get_value_from_json(data, "number_of_target", tmp)) + returnEvent->data.number_of_target = atoi(tmp); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/ports.hpp Mon Apr 10 15:02:24 2017 -0400 @@ -0,0 +1,11 @@ +#pragma once +#include "mbed.h" +//#include "rtos.h" +//#include "lpc17xx.h" + +//Printf to pc and add new line +#define toPc(str, ...) pc.printf(str, ##__VA_ARGS__); pc.printf("\r\n"); +#define cls() pc.printf("\033[2J\r\n"); //Clear pc screen + +extern Serial pc; +extern DigitalOut leds[4];
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/serverEvents.hpp Mon Apr 10 15:02:24 2017 -0400 @@ -0,0 +1,122 @@ +#pragma once +#include "ports.hpp" +#include "EthernetInterface.h" +#include "Websocket.h" +#include "jsonParser.hpp" +#include "dispatcher.hpp" + +#define USING_DHCP_SERVER 0 + +EthernetInterface eth; +Websocket ws("ws://1.0.0.1:8080/"); +Mail<ServerEvent, 30> messages_to_server; +void websocket_message_send(ServerEvent event); + +void ethernet_initialization() { + #if USING_DHCP_SERVER + eth.init(); + #else + char ip_addr[]="1.0.0.2"; + char mask[]="255.255.255.0"; + char gateway[]="1.0.0.1"; + eth.init(ip_addr, mask, gateway); + #endif + + eth.connect(); + toPc("IP Address is %s", eth.getIPAddress()); + toPc("Mask is %s", eth.getNetworkMask()); +} + +void websocket_initialization() { + toPc("Connecting to websocket url: \"ws://1.0.0.1:8080/\""); + ws.connect(); + if (ws.is_connected()) { + ws.send("{\"event\":\"mbed\",\"data\":\"{\"state\": \"connected\"}\"}"); + } +} + +//Thread +void websocket_message_receiver() { + ethernet_initialization(); + websocket_initialization(); + + char msgBuffer[500]; + ServerEvent newEvent; + while(1) { + if (!ws.is_connected()) { + toPc("Disconnected from websocket... Attempting to reconnect"); + websocket_initialization(); + } else { + if (ws.read(msgBuffer)) { + toPc("New message received from server"); + parse_server_event_from_json(msgBuffer, &newEvent); + dispatch_event_from_server(&newEvent); + } + } + Thread::wait(100); + } +} + +//Via thread +void websocket_message_send(ServerEvent event) +{ + ServerEvent *newEvent = messages_to_server.alloc(); + *newEvent = event; + messages_to_server.put(newEvent); +} + +//Direct send +void send_message_to_websocket(ServerEvent *event) +{ + char msgBuffer[500]; + if (strcmp(event->event, "start") == 0) { + sprintf(msgBuffer, "{" + "\"event\":\"%s\"" + "}", event->event); + } else if (strcmp(event->event, "navigate") == 0) { + sprintf(msgBuffer, "{" + "\"event\":\"%s\"," + "\"data\":{" + "\"direction\":\"%s\"" + "}" + "}", event->event, event->data.direction); + } else if (strcmp(event->event, "gun") == 0) { + sprintf(msgBuffer, "{" + "\"event\":\"%s\"," + "\"data\":{" + "\"rfid_code\":\"%s\"" + "}" + "}", event->event, event->data.rfid_code); + } else { //event == report + sprintf(msgBuffer, "{" + "\"event\":\"%s\"," + "\"data\":{" + "\"allies\": %i," + "\"enemies\": %i," + "\"targets\": %i," + "\"averageReflexTime\": %i," + "\"gameLength\": %i," + "\"score\": %i" + "}" + "}", event->event, event->data.allies, event->data.enemies, + event->data.targets, event->data.averageReflexTime, event->data.gameLength, + event->data.score); + } + + //toPc("Sending msg to websocket: %s", msgBuffer); + ws.send(msgBuffer); +} + +//Thread +void websocket_message_sender() +{ + while(1) { + osEvent evt = messages_to_server.get(); + if (evt.status == osEventMail) { + ServerEvent *mail = (ServerEvent*)evt.value.p; + send_message_to_websocket(mail); + messages_to_server.free(mail); + } + Thread::yield(); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Mon Apr 10 15:02:24 2017 -0400 @@ -0,0 +1,86 @@ +#include <vector> +#include "ports.hpp" +#include "serverEvents.hpp" +#include "Target.hpp" +#include "RealXbeeReceiver.hpp" +#include "BtnHandler.hpp" +#include "GameCoordinator.hpp" +#include "FreeMem.h" +#include "MFRC522.h" + +#define MF_RESET D8 + +DigitalOut LedGreen(LED1); +MFRC522 RfChip(p5, p6, p7, p21, p22); + +int main() +{ + cls(); + toPc("Coordinator starting..."); + toPc("Initial Memory: %i", FreeMem()); + + //Starting the different threads + //Websocket + Thread Twebsocket_message_receiver(websocket_message_receiver, osPriorityLow, 2300); + Thread Twebsocket_message_sender(websocket_message_sender, osPriorityLow, 1500); + + //Xbee + RealXbeeReceiver xbeeReceiver; + xbeeReceiver.start(&dispatch_event_from_target); + + //Btn interupts + BtnHandler startBtn(p21, "start"); + BtnHandler nextBtn(p22, "navigate", "next"); + BtnHandler previousBtn(p23, "navigate", "previous"); + + //Creating the targets with their 64 bits address + //Bruno/Max Router + gameCoordinator.targets.push_back(Target(0x0013A200, 0x4086DA0E, 0)); + gameCoordinator.targets.push_back(Target(0x0013A200, 0x4086DA0E, 1)); + //TODO: more targets + + RfChip.PCD_Init(); + + while(1) { + LedGreen = 1; + + // Look for new cards + if ( ! RfChip.PICC_IsNewCardPresent()) + { + toPc("No card detected"); + Thread::wait(500); + continue; + } + + // Select one of the cards + if ( ! RfChip.PICC_ReadCardSerial()) + { + toPc("Cant read serial"); + Thread::wait(500); + //wait_ms(500); + continue; + } + toPc("Printing card"); + LedGreen = 0; + + // Print Card UID + pc.printf("Card UID: "); + for (uint8_t i = 0; i < RfChip.uid.size; i++) + { + pc.printf(" %X02", RfChip.uid.uidByte[i]); + } + pc.printf("\n\r"); + + + //Memory monitoring + // toPc("Thread Twebsocket_message_receiver max stack: %i / %i", Twebsocket_message_receiver.max_stack(), Twebsocket_message_receiver.stack_size()); + // toPc("Thread Twebsocket_message_sender max stack: %i / %i", Twebsocket_message_sender.max_stack(), Twebsocket_message_sender.stack_size()); + // toPc("Thread gameCoordinator.Tget_next_round max stack: %i / %i", gameCoordinator.Tget_next_round.max_stack(), gameCoordinator.Tget_next_round.stack_size()); + // toPc("Thread xbeeReceiver.message_handler_thread max stack: %i / %i", xbeeReceiver.message_handler_thread.max_stack(), xbeeReceiver.message_handler_thread.stack_size()); + // toPc("Thread xbeeReceiver.message_reader_thread max stack: %i / %i", xbeeReceiver.message_reader_thread.max_stack(), xbeeReceiver.message_reader_thread.stack_size()); + // toPc("Free Memory: %i", FreeMem()); + Thread::wait(1000); + } + //Thread::wait(osWaitForever); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/GameCoordinator.cpp Mon Apr 10 15:02:24 2017 -0400 @@ -0,0 +1,58 @@ +#include "GameCoordinator.hpp" + +GameCoordinator gameCoordinator; + +//TODO: move that to .cpp +GameCoordinator::GameCoordinator() +{ + //Adding all the game modes to the game_modes vector + //TODO: can we avoid new operator ? + ReflexMode *reflex_mode = new ReflexMode(&targets); + SpeedMode *speed_mode = new SpeedMode(&targets); + game_modes.push_back(reflex_mode); + game_modes.push_back(speed_mode); + + //Starting internal thread + Tget_next_round = Thread(osPriorityNormal, 800); + Tget_next_round.start(this, &GameCoordinator::get_next_round); +} + +void GameCoordinator::start_game(ServerData *configs) +{ + current_game = game_modes[configs->game_id]; + target_timeout = configs->max_reflex_time; + number_of_targets = configs->number_of_target; + ticker.attach(callback(this, &GameCoordinator::ticker_callback), current_game->getTimeBetweenTargets() / 1000.0); +} + +void GameCoordinator::stop_game() +{ + ticker.detach(); + LPC_TIM3->TC = 0; + //TODO: send stats back to server +} + +void GameCoordinator::ticker_callback() +{ + Tget_next_round.signal_set(0x1); +} + +//Thread +void GameCoordinator::get_next_round() +{ + while (1) + { + Thread::signal_wait(0x1); + Target *nextTarget = current_game->GetNextTarget(); + if (nextTarget == NULL || number_of_targets-- <= 0) + { + stop_game(); + } + else + { + //TODO: how to select enemy or ally ? + nextTarget->rise(rand() % 2, target_timeout); + } + Thread::yield(); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Target.cpp Mon Apr 10 15:02:24 2017 -0400 @@ -0,0 +1,32 @@ +#include "Target.hpp" + +RealXbeeTransmitter xbee_transmitter; + +Target::Target(int address_msb, int address_lsb, int target_number) +{ + get_address_for_xbee(address_msb, address_lsb); + this->target_number = target_number; +} + +void Target::rise(int mode, int timeout) +{ + toPc("Rising target %i!", target_number); + vector<uint8_t> message; + message.push_back(target_number); + message.push_back(mode); + message.push_back((timeout >> 8) & 0xFF); + message.push_back(timeout && 0xFF); + xbee_transmitter.transmit(message, addr_table); +} + +void Target::get_address_for_xbee(int address_msb, int address_lsb) +{ + addr_table[0] = address_msb >> 24; + addr_table[1] = (address_msb >> 16) & 0xFF; + addr_table[2] = (address_msb >> 8) & 0xFF; + addr_table[3] = (address_msb) & 0xFF; + addr_table[4] = address_lsb >> 24; + addr_table[5] = (address_lsb >> 16) & 0xFF; + addr_table[6] = (address_lsb >> 8) & 0xFF; + addr_table[7] = (address_lsb) & 0xFF; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ports.cpp Mon Apr 10 15:02:24 2017 -0400 @@ -0,0 +1,6 @@ +#include "ports.hpp" + +Serial pc(USBTX, USBRX); +DigitalOut leds[4] = { + DigitalOut(LED1), DigitalOut(LED2), DigitalOut(LED3), DigitalOut(LED4) +}; \ No newline at end of file