aboutsummaryrefslogtreecommitdiff
path: root/main/i2c.c
blob: c499727a5dd0d02e77262e14947e90629f771a21 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#include <FreeRTOS.h>
#include <task.h>
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <pico/stdlib.h>
#include <hardware/i2c.h>

#include "i2c.h"
#include "pb-mod.h"
#include "config.h"
#include "pb-buf.h"
#include "pb-send.h"

typedef struct {
	i2c_addr_t sender; //!< I2C address of sender
	pb_global_state_t state; //!< global state
} puzzle_module_t;

static pb_global_state_t _global_state = PB_GS_IDLE;
puzzle_module_t modules[CFG_PB_MOD_MAX];
// stolen from lib/pico-sdk/src/rp2_common/hardware_i2c/i2c.c
#define i2c_reserved_addr(addr) (((addr) & 0x78) == 0 || ((addr) & 0x78) == 0x78)
size_t modules_size = 0;

static void bus_scan() {
	pb_buf_t buf = pb_send_magic_req();

	// check for all 7-bit addresses
	uint16_t addr_max = 1 << 7;
	for (uint16_t addr = 0x00; addr < addr_max; addr++) {
		if (i2c_reserved_addr(addr)) continue;
		if (addr == PB_MOD_ADDR) continue;

		pb_i2c_send(addr, (uint8_t *) buf.data, buf.size);
	}

	pb_buf_free(&buf);
}

static void update_state() {
	int idle = 0, playing = 0, solved = 0;

	// count # of modules in each state
	for (size_t i = 0; i < modules_size; i++) {
		pb_global_state_t state = modules[i].state;
		if (state == PB_GS_IDLE) idle++;
		else if (state == PB_GS_PLAYING) playing++;
		else if (state == PB_GS_SOLVED) solved++;
	}

	if (idle == modules_size) { // if all modules are in PB_GS_IDLE
		pb_hook_mod_state_write(PB_GS_IDLE);
	} else if (solved == modules_size) { // if all modules are in PB_GS_SOLVED
		pb_hook_mod_state_write(PB_GS_SOLVED);
	} else {
		pb_hook_mod_state_write(PB_GS_PLAYING);
	}

	// if a module is still playing, don't promote a next one to playing module
	if (playing > 0) return;

	for (size_t i = 0; i < modules_size; i++) {
		// find first module that is idle
		pb_global_state_t	module_state = modules[i].state;
		if (module_state != PB_GS_IDLE) continue;

		pb_buf_t buff = pb_send_state_set(PB_GS_PLAYING);
		pb_i2c_send(modules[i].sender, (uint8_t*)buff.data, buff.size);
		pb_buf_free(&buff);
	}
}

static void state_exchange() {
	update_state();
	pb_buf_t buf = pb_send_state_req();
	for (size_t i = 0; i < modules_size; i++)
		pb_i2c_send(modules[i].sender, (uint8_t *) buf.data, buf.size);
	pb_buf_free(&buf);
}

void bus_task() {
	// do a scan of the bus
	bus_scan();

	while(1) {
		// send my state to all puzzle modules
		state_exchange();

		// wait 1 second
		vTaskDelay(1e3 / portTICK_PERIOD_MS);
	}
}

/**
 * \ingroup main_pb_override
 * \anchor main_route_cmd_magic_res
 *
 * This function registers the I2C address of the puzzle module that replied to
 * the \c MAGIC \c REQ command into a list of "known puzzle modules", which are
 * then periodically updated during gameplay.
 *
 * \note Up to \ref CFG_PB_MOD_MAX puzzle modules can be registered
 * simultaniously.
 */
void pb_route_cmd_magic_res(pb_msg_t * msg) {
	if (modules_size == CFG_PB_MOD_MAX) return;
	modules[modules_size++] = (puzzle_module_t) {
		.sender = msg->sender,
		.state = PB_GS_NOINIT,
	};
	printf("i2c: registered puzzle module w/ address 0x%02x\n", msg->sender);
}

void pb_route_cmd_state_res(pb_msg_t * msg) {
	pb_cmd_state_t * cmd = msg->cmd;
	i2c_addr_t sender = msg->sender;

	for (size_t i = 0; i < modules_size; i++) {
		if (modules[i].sender != sender) continue;
		modules[i].state = cmd->state;
	}
}

pb_global_state_t pb_hook_mod_state_read() {
	return _global_state;
}

void pb_hook_mod_state_write(pb_global_state_t state) {
	_global_state = state;
}

void pb_route_cmd_prop_req(pb_msg_t * msg) {
	// send modules using buf
	pb_cmd_prop_t cmd = msg->cmd;

	pb_buf_t buf;

	if(cmd == "PROPID_AJHDKADLHL") {
		pb_send_reply(msg,buf);
	}

	pb_buf_free(buf);
}

void pb_route_cmd_prop_set(pb_msg_t * msg) {
	// scan bus again.
	pb_cmd_prop_t cmd = msg->cmd;

	if(cmd == "") {

	}
}